我刚刚读完 R 序言中的范围,对 <<-的作业很好奇。
<<-
手册中展示了 <<-的一个(非常有趣的)例子,我觉得我理解了。我仍然缺少的是什么时候这可以是有用的上下文。
所以我想从你们那里读到的是关于什么时候使用 <<-可以变得有趣/有用的例子(或链接到例子)。使用它可能有什么危险(它看起来很容易忘记) ,以及任何你想要分享的技巧。
我使用 <<-的一个地方是在使用 tcl/tk 的简单 GUI 中。最初的一些示例中就有这样的例子——因为需要区分局部变量和全局变量的有状态性。举个例子
library(tcltk) demo(tkdensity)
它使用 <<-。否则我同意马雷克:)——谷歌搜索可以帮助。
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())} plot(f(1000,0),typ="l")
将 <<-视为等价于 assign会有所帮助(如果将该函数中的 inherits参数设置为 TRUE)。assign的好处是它允许您指定更多的参数(例如环境) ,所以在大多数情况下,我更喜欢使用 assign而不是 <<-。
assign
inherits
TRUE
使用 <<-和 assign(x, value, inherits=TRUE)意味着“搜索所提供环境的封闭环境,直到遇到变量‘ x’”换句话说,它将按顺序遍历整个环境,直到找到一个具有该名称的变量,并将其赋值给该变量。这可以在函数范围内,也可以在全局环境中。
assign(x, value, inherits=TRUE)
为了理解这些函数的作用,您还需要理解 R 环境(例如使用 search)。
search
当我运行一个大型模拟时,我经常使用这些函数,我想保存中间结果。这允许您在给定函数或 apply循环的范围之外创建对象。这非常有帮助,特别是如果您担心一个大的循环意外结束(例如数据库断开) ,在这种情况下,您可能会失去整个过程中的一切。这相当于在长时间运行的过程中将结果写入数据库或文件,只不过它将结果存储在 R 环境中。
apply
我的主要警告是: 要小心,因为您现在使用的是全局变量,特别是在使用 <<-时。这意味着最终可能出现这样的情况: 函数使用来自环境的对象值,而您希望它使用作为参数提供的对象值。这是函数式编程试图避免的主要问题之一(参见 副作用)。为了避免这个问题,我将我的值分配给一个唯一的变量名(使用带有集合或唯一参数的粘贴) ,这个变量名从来没有在函数中使用过,只是用于缓存,以防以后需要恢复(或者对中间结果进行一些元分析)。
<<-与闭包一起维护状态非常有用。下面是我最近一篇论文的一个部分:
闭包是由另一个函数编写的函数 所谓的因为他们 附上的环境的父母 中的所有变量和参数 这是很有用的,因为它允许我们有两个层次的 一级参数(父级)控制 函数工作。另一个级别(子级)完成工作 下面的例子展示了如何使用这个想法来生成一系列 父函数(power)创建子函数 (square和 cube) ,实际上做艰苦的工作。
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
i
new_counter <- function() { i <- 0 function() { # do something useful, then ... i <<- i + 1 i } }
新函数是一个闭包,它的环境是一个封闭的环境。当运行闭包 counter_one和 counter_two时,每个闭包都在其封闭环境中修改计数器,然后返回当前计数。
counter_one
counter_two
counter_one <- new_counter() counter_two <- new_counter() counter_one() # -> [1] 1 counter_one() # -> [1] 2 counter_two() # -> [1] 1
在这个问题上,我想指出的是,当在 for 循环中应用(错误地) <<-操作符时,它的行为会很奇怪(可能还有其他情况)。根据以下守则:
fortest <- function() { mySum <- 0 for (i in c(1, 2, 3)) { mySum <<- mySum + i } mySum }
您可能期望函数返回预期的和,6,但是它返回0,创建一个全局变量 mySum并赋值3。我不能完全解释这里发生了什么,但是可以肯定的是,for 循环的主体是 没有,一个新的范围‘级别’。相反,似乎 R 看到的是 fortest函数之外,找不到要赋值的 mySum变量,所以创建一个变量并赋值1,这是第一次通过循环。在随后的迭代中,赋值中的 RHS 必须引用(不变的)内部 mySum变量,而 LHS 引用全局变量。因此,每个迭代都将全局变量的值重写为该迭代的 i值,因此它在从函数退出时具有值3。
mySum
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
我使用它是为了在全局环境中更改 map ()中的一个对象。
a = c(1,0,0,1,0,0,0,0)
假设我想得到一个向量,它是 c (1,2,3,1,2,3,4,5) ,也就是说,如果有一个1,让它1,否则加1直到下一个1。
map( .x = seq(1,(length(a))), .f = function(x) { a[x] <<- ifelse(a[x]==1, a[x], a[x-1]+1) }) a [1] 1 2 3 1 2 3 4 5