编写函数 Code style 一般function后面都会有一个花括号{},用于填函数体,但是如果if 语句后面的函数句太短的时候,可以省略花括号: 如下: y <> x <>if (y <>'Too low' else 'Too high'
书的作者只推荐非常简短的if语句。否则,还是要用完整的花括号来更容易阅读 if (y <>20 ) { x <>'Too low' } else { x <>'Too high' }
Function arguments 函数的参数通常分为两大类:一组提供要计算的数据,另一组提供控制计算细节的参数。(data,detail) 例如: 在log()中,数据是x,而detail是对数的底。 在mean()中,数据是x,而细节是从结束(修剪)和如何处理缺失值(na.rm)中裁剪多少数据。 在t.test()中,数据是x和y,测试的细节是alternative, mu, paired, var.equal, 和 conf.level。 在str_c()中,您可以向…提供任意数量的字符串。,连接的细节由sep和collapse控制。 通常,数据参数应该放在首位。细节参数应该在末尾,并且通常应该具有默认值。您指定一个默认值的方式与使用命名参数调用函数的方式相同 # Compute confidence interval around mean using normal approximation mean_ci <>function (x, conf = 0.95 ) { se <- sd(x) >- sd(x) > alpha <>1 - conf mean(x) + se * qnorm(c(alpha / 2 , 1 - alpha / 2 )) } x <>100 ) mean_ci(x)#> [1] 0.498 0.610 mean_ci(x, conf = 0.99 )#> [1] 0.480 0.628
当您调用一个函数时,您通常会忽略数据参数的名称,因为它们非常常用。如果重写细节参数的默认值,应该使用参数全名: # Good mean(1 :10 , na.rm = TRUE )##下面的形式就不友好 # Bad mean(x = 1 :10 , , FALSE ) mean(, TRUE , x = c(1 :10 , NA ))
Checking values 当你开始写更多的函数时,你最终会忘记你的函数是如何工作的。此时,使用无效的输入调用函数很容易。为了避免这个问题,明确约束通常是有用的。例如,假设您编写了一些计算加权汇总统计的函数。 wt_mean <>function (x, w) { sum(x * w) / sum(w) } wt_var <>function (x, w) { mu <> sum(w * (x - mu) ^ 2 ) / sum(w) } wt_sd <>function (x, w) { sqrt (wt_var(x, w)) }
你会发现x和w长度不同,但是R里面不会报错,因为R里面可以增加循环 wt_mean (1:6 , 1:3) #> [1] 7.67
It’s good practice to check important preconditions, and throw an error (with stop()), if they are not true:
wt_mean <>function (x, w) { if (length(x) != length(w)) { stop('`x` and `w` must be the same length' , call. = FALSE ) } sum(w * x) / sum(w) }
mark 如果你也加了一个na.rm参数,你可能不会仔细检查。 wt_mean <>function (x, w, na.rm = FALSE ) { if (!is .logical(na.rm)) { stop ('`na.rm` must be logical' ) } if (length(na.rm) != 1 ) { stop ('`na.rm` must be length 1' ) } if (length(x) != length(w)) { stop ('`x` and `w` must be the same length' , call . = FALSE ) } if (na.rm) { miss <>is .na(x) | is .na(w) x <> w <> } sum(w * x) / sum(w) }
这是很多额外的工作,几乎没有额外的收获。一个有用的折衷方案是内置的stopifnot():它检查每个参数是否为真,如果没有,则生成一个通用的错误消息. wt_mean <>function (x, w, na.rm = FALSE ) { stopifnot(is .logical(na.rm), length(na.rm) == 1 ) stopifnot(length(x) == length(w)) if (na.rm) { miss <>is .na(x) | is .na(w) x <> w <> } sum(w * x) / sum(w) } wt_mean(1 :6 , 6 :1 , na.rm = 'foo' ) #> Error in wt_mean(1 :6 , 6 :1 , na.rm = 'foo' ): is .logical(na.rm) is not TRUE
mark Return values 函数返回的值通常是它计算的最后一个语句,但是您可以使用return()选择返回之前的值。作者认为最好保存return()的使用,以表明您可以使用更简单的解决方案提前返回。 complicated_function <>function (x, y, z) { if (length(x) == 0 || length(y) == 0 ) { return (0 ) } # Complicated code here }
当你有一个if语句,包含一个复杂块和一个简单块。例如,您可以编写这样的if语句 f <>function () { if (x) { # Do # something # that # takes # many # lines # to # express } else { # return something short } }
但是如果第一个代码块很长,当你写到else,你已经忘记了条件。那么你可以以一种简单的方式重写: f <>function () { if (!x) { return (something_short) } # Do # something # that # takes # many # lines # to # express }