约翰·钱伯斯(John Chambers)在《Software for Data Analysis: Programming with R》一书中强调,函数的编写通常不应考虑其副作用;相反,函数应该在不修改其调用环境中的任何变量的情况下返回值.相反,使用数据编写好的脚本.表对象应特别避免使用对象赋值<-,通常用于存储函数的结果.

首先是一个技术问题.想象一下,一个名为proc1的R函数接受data.table对象x作为其参数(可能还有其他参数).proc1返回NULL,但使用:=修改x.据我所知,proc1呼叫proc1(x=x1)复制x1只是因为promise 的工作方式.然而,如下所示,原始对象x1仍然被proc1修改.这是怎么回事?

> require(data.table)
> x1 <- CJ(1:2, 2:3)
> x1
   V1 V2
1:  1  2
2:  1  3
3:  2  2
4:  2  3
> proc1 <- function(x){
+ x[,y:= V1*V2]
+ NULL
+ }
> proc1(x1)
NULL
> x1
   V1 V2 y
1:  1  2 2
2:  1  3 3
3:  2  2 4
4:  2  3 6
> 

此外,使用proc1(x=x1)似乎并不比直接在x上执行程序慢,这表明我对promise 的模糊理解是错误的,它们以一种通过引用的方式工作:

> x1 <- CJ(1:2000, 1:500)
> x1[, paste0("V",3:300) := rnorm(1:nrow(x1))]
> proc1 <- function(x){
+ x[,y:= V1*V2]
+ NULL
+ }
> system.time(proc1(x1))
   user  system elapsed 
   0.00    0.02    0.02 
> x1 <- CJ(1:2000, 1:500)
> system.time(x1[,y:= V1*V2])
   user  system elapsed 
   0.03    0.00    0.03 

所以,考虑到传递数据.函数的table参数不会增加时间,这使得为数据编写过程成为可能.表对象,结合了数据的速度.表和函数的可推广性.然而,考虑到John Chambers所说的,函数不应该有副作用,用R编写这种类型的过程编程真的"可以"吗?他为什么说副作用是"坏的"?如果我要无视他的建议,我应该注意哪些trap ?我该怎么写"好"的数据呢.表格程序?

推荐答案

是的,在data.tables中添加、修改、删除列是由reference完成的.从某种意义上说,这是一个good的东西,因为data.table通常包含大量数据,每次对其进行更改时,重新分配所有数据将非常耗费内存和时间.另一方面,它是bad的东西,因为它违背了no-side-effect函数式编程方法,R试图通过默认使用pass-by-value来推广no-side-effect函数式编程方法.由于编程没有副作用,在调用函数时不必担心:您可以放心,您的输入或环境不会受到影响,您可以只关注函数的输出.它很简单,因此很舒服.

当然,如果你知道自己在做什么,就可以无视约翰·钱伯斯的建议.关于写"好"数据.表格程序,以下是我将要考虑的几个规则,作为限制复杂性和副作用数量的一种方法:

  • 一个函数不应修改多个表,即修改该表应是唯一的副作用,
  • 如果一个函数修改了一个表,那么将该表作为该函数的输出.当然,你不会想重新分配它:只运行do.something.to(table),而不是table <- do.something.to(table).如果该函数有另一个("实")输出,那么在调用result <- do.something.to(table)时,很容易想象您可能会将注意力集中在输出上,而忘记调用该函数会对您的表产生副作用.

虽然"单输出/无副作用"功能是R中的标准,但上述规则允许"单输出或副作用".如果你同意副作用某种程度上是一种输出形式,那么你会同意我没有太多地扭曲规则,而是松散地坚持R的单输出函数编程风格.允许功能产生多种副作用将是一种更大的挑战;并不是说你做不到,但如果可能的话,我会尽量避免.

R相关问答推荐

extract()函数不处理stanfit对象,我用错了吗?

用apply/map/etch替换循环以加快速度

R中的Fasttext langue_identification返回太多参数-如何与文本匹配?

使用map()内的公式()创建多个公式

如何将多个数据帧附加到R中的多个相应的CSV文件中?

使用case_when和Mutate搜索多个列以寻找条件

卸载安装了BRM的模型发出的警告

从API中抓取R数据SON

从嵌套列表中智能提取线性模型系数

如何按排序顺序打印一个框架中所有精确的唯一值?

pickerInput用于显示一条或多条geom_hline,这些线在图中具有不同 colored颜色

如何优化向量的以下条件赋值?

使用R中的正则表达式将一列分割为多列

如何基于两个条件从一列中提取行

有没有办法使用ggText,<;Sub>;&;<;sup>;将上标和下标添加到同一元素?

Ggplot2中geom_tile的动态zoom

如何在PrePlot()中将多个元素设置为斜体

如何在R中使用混合GAM模型只对固定的影响因素进行适当的预测?

根据r中每行中的日期序列,使用列名序列创建新列

以任意顺序提取具有多个可能匹配项的组匹配项