最近群里契卡大佬分享了一本薄薄的小册子:《The tidyverse style guide 》,是Hadley Wickham 自己写的关于Tidyverse的编程范式。看了第一部分觉得甚好,在这里随便写下翻译,作为自己的cheatsheet。(第二部分是关于R包里面的范式,暂时还用不到)
书来源:
The tidyverse style guide
前言:
- 有两个R包支持Tidyverse的编程范式引导:
styler
和lintr
。
Charpter 1:Files
-
文件的命名要避免特殊符号,坚持用数字、字母、
-
、_
。# Good fit_models.R utility_functions.R # Bad fit models.R foo.r stuff.r
-
如果文件是有顺序的,在文件名之前带上数字前缀。如果你有超过10个文件,左边留一个0.
00_download.R 01_explore.R ... 09_model.R 10_visualize.R
如果中间遗留了什么步骤,可以尝试用
02a
或者02b
这种。但还是建议重新命名注意不同操作系统之间的大小写敏感问题。最好文件名都是小写的,永远不要有两个文件的名字只是大小写不一样。
-
用
-
和=
的注释行来把你的文件分成一个个块# Load data --------------------------- # Plot data ---------------------------
如果你的脚本需要附加包,在一开始就载入他们。这会比你在整个文件中到处加载代码或者在启动文件(比如说
.Rprofile
)中隐藏地加载要好。
Charpter 2:Syntax
-
变量和函数的名字应该只用小写字母,数字和
_
。# Good day_one day_1 # Bad DayOne dayone
基本的R会用在函数名(
contrib.url()
)或者类名(data.frame
) 中用到.
,但最好只为S3系统保留点这个符号。-
变量名最好是名词而函数名最好是动词。
# Good day_one # Bad first_day_of_the_month djm1
-
避免重复使用常见的函数和变量
# Bad T <- FALSE c <- 10 mean <- function(x) sum(x)
-
像平常的英语语法一样,经常在逗号后面放一个空格,而不是在逗号前面
# Good x[, 1] # Bad x[,1] x[ ,1] x[ , 1]
-
对于常规函数的调用,不要在圆括号的里面或者外面放空格
# Good mean(x, na.rm = TRUE) # Bad mean (x, na.rm = TRUE) mean( x, na.rm = TRUE )
-
当使用
if
,for
, 或者while
的时候,在()
的前面和后面放空格# Good if (debug) { show(x) } # Bad if(debug){ show(x) }
-
Place a space after
()
used for function arguments:# Good function(x) {} # Bad function (x) {} function(x){}
-
大部分中缀操作符 (
==
,+
,-
,<-
, etc.) 的前后都应该有空格# Good height <- (feet * 12) + inches mean(x, na.rm = 10) # Bad height<-feet*12+inches mean(x, na.rm=10)
-
但也有些例外
-
有高优先权的一些操作符:
::
,:::
,$
,@
,[
,[[
,^
, unary-
, unary+
, and:
。# Good sqrt(x^2 + y^2) df$z x <- 1:10 # Bad sqrt(x ^ 2 + y ^ 2) df $ z x <- 1 : 10
unary指的是一元运算符,unary - 指的应该是 -2,-3这种负号。
-
当右侧的表达式是单个identifier,单边的公式
# Good ~foo tribble( ~col1, ~col2, "a", "b" ) # Bad ~ foo tribble( ~ col1, ~ col2, "a", "b" )
-
当右边的表达式很复杂的时候,单边公式应当有个空格
# Good ~ .x + .y # Bad ~.x + .y
-
有tidy计算符的时候,比如
!!
和!!!
# Good call(!!xyz) # Bad call(!! xyz) call( !! xyz) call(! !xyz)
-
帮助符
# Good package?stats ?mean # Bad package ? stats ? mean
-
-
为了对齐
=
和<-
,可以加一些额外的空格# Good list( total = a + b + c, mean = (a + b + c) / n ) # Also fine list( total = a + b + c, mean = (a + b + c) / n )
-
一个函数的参数往往分成两大类, 一种是用来放要计算的数据的,另一种是控制计算的细节的。当调用一个函数的使用,你可以选择性地忽略掉数据参数的名字,因为他们很常用。
# Good mean(1:10, na.rm = TRUE) # Bad mean(x = 1:10, , FALSE) mean(, TRUE, x = c(1:10, NA))
避免参数的部分匹配
-
对于花括号的使用有一些准则
-
{
应该是行的最后一个字符。一些相关的代码(e.g., anif
clause, a function declaration, a trailing comma, …) 必须和{
在同一行 - 缩进应该是两个空格
-
}
应该是行的第一个字符
# Good if (y < 0 && debug) { message("y is negative") } if (y == 0) { if (x > 0) { log(x) } else { message("x is negative or zero") } } else { y^x } test_that("call1 returns an ordered factor", { expect_s3_class(call1(x, y), c("factor", "ordered")) }) tryCatch( { x <- scan() cat("Total: ", sum(x), "\n", sep = "") }, interrupt = function(e) { message("Aborted by user") } ) # Bad if (y < 0 && debug) { message("Y is negative") } if (y == 0) { if (x > 0) { log(x) } else { message("x is negative or zero") } } else { y ^ x }
-
-
如果函数在一行的话,也可以不加花括号。只要其没有副作用(但像print,stop这种似乎是由副作用的)
# Good y <- 10 x <- if (y < 20) "Too low" else "Too high"
-
会影响控制流(like
return()
,stop()
orcontinue
)的函数调用应该在其自己的 {} 块内# Good if (y < 0) { stop("Y is negative") } find_abs <- function(x) { if (x > 0) { return(x) } x * -1 } # Bad if (y < 0) stop("Y is negative") if (y < 0) stop("Y is negative") find_abs <- function(x) { if (x > 0) return(x) x * -1 }
-
每行的代码应该控制在80个字符左右,这样才方便后来的打印。要学会断行。
# Good do_something_very_complicated( something = "that", requires = many, arguments = "some of which may be long" ) # Bad do_something_very_complicated("that", requires, many, arguments, "some of which may be long" )
-
对于一些很常见的参数,你可以不写参数名字。如果不写参数名的参数很短,可以放在一行里面。
map(x, f, extra_argument_a = 10, extra_argument_b = c(1, 43, 390, 210209) )
-
如果几个参数很相关,那么就可以放在一行里面,比如说
paste
或者stop
这种。# Good paste0( "Requirement: ", requires, "\n", "Result: ", result, "\n" ) # Bad paste0( "Requirement: ", requires, "\n", "Result: ", result, "\n")
-
在变量赋值的时候使用
<-
而不是=
# Good x <- 5 # Bad x = 5
不要用
;
……(个人觉得)-
在用引号包字符串的时候,用
""
而不是''
。唯一的例外是你字符串里面有双括号,而没有单括号。# Good "Text" 'Text with "quotes"' '<a href="http://style.tidyverse.org">A link</a>' # Bad 'Text' 'Text with "double" and \'single\' quotes'
在写代码的时候,注释是用来标记重要的发现和决定的。如果你需要注释来解释你的代码在干什么,那么就要考虑重写你的代码,从而让你的代码更加清楚。如果你发现你的注释比代码多了,考虑用Rmarkdown。
Charpter 3:Functions
-
和之前说的一样,函数名应该用动词
# Good add_row() permute() # Bad row_adder() permutation()
-
函数里面的参数要对齐
# Good long_function_name <- function(a = "a long argument", b = "another argument", c = "another long argument") { # As usual code is indented by two spaces. } # Bad long_function_name <- function(a = "a long argument", b = "another argument", c = "another long argument") { # Here it's hard to spot where the definition ends and the # code begins }
-
return只用在早期的返回值上,后面的返回值依赖于R的自动返回。
# Good find_abs <- function(x) { if (x > 0) { return(x) } x * -1 } add_two <- function(x, y) { x + y } # Bad add_two <- function(x, y) { return(x + y) }
-
return应该在其自己那行
# Good find_abs <- function(x) { if (x > 0) { return(x) } x * -1 } # Bad find_abs <- function(x) { if (x > 0) return(x) x * -1 }
-
如果你的函数主要是为了其副作用(打印,画图或者保存到硬盘里面),其应该让第一个参数隐式。这可以让这个函数作为管道的一部分。
print
函数应该经常是这样的。一个来自 httr 的例子print.url <- function(x, ...) { cat("Url: ", build_url(x), "\n", sep = "") invisible(x) }
-
在代码中,注释应该是解释为什么,而非你要做什么或者怎么做。每个注释都应该以注释符号# 开头,再加上一个空格
# Good # Objects like data frames are treated as leaves x <- map_if(x, is_bare_list, recurse) # Bad # Recurse only with bare lists x <- map_if(x, is_bare_list, recurse)
-
Comments should be in sentence case, and only end with a full stop if they contain at least two sentences:
# Good # Objects like data frames are treated as leaves x <- map_if(x, is_bare_list, recurse) # Do not use `is.list()`. Objects like data frames must be treated # as leaves. x <- map_if(x, is_bare_list, recurse) # Bad # objects like data frames are treated as leaves x <- map_if(x, is_bare_list, recurse) # Objects like data frames are treated as leaves. x <- map_if(x, is_bare_list, recurse)