Answers:
<<-
与闭包一起维护状态最有用。这是我最近的一篇文章的一部分:
闭包是由另一个函数编写的函数。闭包之所以称为闭包,是因为它们封闭了父函数的环境,并且可以访问该函数中的所有变量和参数。这很有用,因为它允许我们具有两个级别的参数。一级参数(父级)控制函数的工作方式。另一个级别(孩子)负责这项工作。以下示例显示了如何使用此思想生成幂函数系列。父函数(power
)创建实际完成工作的子函数(square
和cube
)。
power <- function(exponent) {
function(x) x ^ exponent
}
square <- power(2)
square(2) # -> [1] 4
square(4) # -> [1] 16
cube <- power(3)
cube(2) # -> [1] 8
cube(4) # -> [1] 64
在两个级别上管理变量的能力还使得可以通过允许函数在其父代环境中修改变量来维持函数调用之间的状态。管理不同级别变量的关键是双箭头分配运算符 <<-
。与通常<-
在当前级别上起作用的通常的单箭头分配()不同,双箭头运算符可以在父级别中修改变量。
这使得可以维护一个计数器,该计数器记录一个函数被调用的次数,如下例所示。每次new_counter
运行时,它都会创建一个环境,i
在此环境中初始化计数器,然后创建一个新函数。
new_counter <- function() {
i <- 0
function() {
# do something useful, then ...
i <<- i + 1
i
}
}
新功能是一个闭合,其环境是闭合环境。运行闭包counter_one
和时counter_two
,每个计数器都会在其封闭环境中修改计数器,然后返回当前计数。
counter_one <- new_counter()
counter_two <- new_counter()
counter_one() # -> [1] 1
counter_one() # -> [1] 2
counter_two() # -> [1] 1
可以认为<<-
等效于assign
(如果inherits
将该函数中的参数设置为TRUE
)。的好处assign
是,它允许您指定更多的参数(例如环境),所以我更喜欢使用assign
在<<-
大多数情况下。
使用<<-
和assign(x, value, inherits=TRUE)
表示“搜索提供的环境的封闭环境,直到遇到变量'x'。” 换句话说,它将继续按顺序遍历环境,直到找到具有该名称的变量,并将其分配给该变量为止。这可以在功能范围内,也可以在全局环境中。
为了了解这些功能的作用,您还需要了解R环境(例如使用search
)。
在运行大型仿真时,我经常使用这些功能,并且我想保存中间结果。这使您可以在给定函数范围之外创建对象,或者apply
循环。这非常有帮助,尤其是当您担心大循环意外结束(例如数据库断开连接)时,在这种情况下,您可能会丢失过程中的所有内容。这等效于在长时间运行的过程中将结果写到数据库或文件中,只是将结果存储在R环境中。
我的主要警告是:请小心,因为您现在正在使用全局变量,尤其是在使用时<<-
。这意味着您可能会遇到以下情况:函数希望使用环境中的对象值,而您期望它使用的是作为参数提供的对象值。这是函数式编程要避免的主要事情之一(请参阅副作用)。通过将值分配给唯一的变量名(使用带有设定值或唯一参数的粘贴)来避免此问题,该变量名从未在函数中使用,而仅用于缓存,以防万一我以后需要恢复(或执行一些元操作) -中间结果分析)。
我使用的一个地方<<-
是使用tcl / tk的简单GUI。一些初始示例具有此功能-因为您需要在局部变量和全局变量之间进行区分以确保状态充分。例如看
library(tcltk)
demo(tkdensity)
使用<<-
。否则,我同意Marek的意见:)-Google搜索可以提供帮助。
tkdensity
在R 3.6.0中找不到。
关于这个问题,我想指出的是,在<<-
for循环中(错误地)应用运算符时,运算符的行为会很奇怪(可能还有其他情况)。给出以下代码:
fortest <- function() {
mySum <- 0
for (i in c(1, 2, 3)) {
mySum <<- mySum + i
}
mySum
}
您可能希望函数将返回预期的总和6,但是它返回0,并mySum
创建一个全局变量并将其赋值为3。循环不是新的作用域“级别”。取而代之的是,R似乎不在fortest
函数之外,找不到mySum
要分配给的变量,因此在循环中第一次创建一个并分配值1。在随后的迭代中,赋值中的RHS必须引用(未更改的)内部mySum
变量,而LHS必须引用全局变量。因此,每次迭代都会将全局变量的值覆盖为该迭代的值i
,因此在函数退出时其值为3。
希望这对某人有帮助-今天让我难过了几个小时!(顺便说一句,只需替换<<-
为<-
,该功能将按预期工作)。
mySum
从不增加,而仅global mySum
。因此,在for循环的每次迭代中,全局mySum
值都会获得0 + i
。您可以使用跟随debug(fortest)
。
<-
如果您只想更新函数内的局部变量,则只需在函数内的所有位置一致使用即可。
编写引用方法时,该<<-
运算符对于引用类也可能很有用。例如:
myRFclass <- setRefClass(Class = "RF",
fields = list(A = "numeric",
B = "numeric",
C = function() A + B))
myRFclass$methods(show = function() cat("A =", A, "B =", B, "C =",C))
myRFclass$methods(changeA = function() A <<- A*B) # note the <<-
obj1 <- myRFclass(A = 2, B = 3)
obj1
# A = 2 B = 3 C = 5
obj1$changeA()
obj1
# A = 6 B = 3 C = 9