用于替换非本地列表中的值的赋值

Kon*_*lph 17 environment r list assign

[[<- 在非本地对象上使用时,列表和环境的行为会有所不同:

lst = list()
env = new.env()

(function () lst[['x']] = 1)()
(function () env[['x']] = 1)()
Run Code Online (Sandbox Code Playgroud)
lst
# list()

as.list(env)
# $x
# [1] 1
Run Code Online (Sandbox Code Playgroud)

换句话说,如果目标[[<-是环境,它会修改(非本地)环境,但如果它是矢量/列表,则会创建一个新的本地对象.

我想知道两件事:

  1. 为什么这种行为上的差异?
  2. 有没有一种方法可以实现列表和环境相同的结果,而不使用<<-

关于(1),我知道列表和环境之间存在多种差异(特别是我知道环境不会被复制)但文档中没有提到为什么[[<-两者之间的语义不同 - 特别是为什么运营商将在不同的范围内运作.这是一个错误吗?至少它是反直觉的,并且需要一些非平凡的实现恶作剧.1

关于(2),显而易见的解决方案当然是使用<<-:

(function () lst[['x']] <<- 1)()
Run Code Online (Sandbox Code Playgroud)

但是,我更喜欢严格理解差异,而不是仅仅解决它们.此外,我到目前为止使用assign而不是,<<-我更喜欢这个,因为它允许我更好地控制作业的范围(特别是因为我可以指定inherits = FALSE.<<-对我来说太多伏都教了.

但是,上述内容无法解决(据我所知)使用,assign因为assign只适用于环境,而不是列表.特别是,虽然assign('x', 1, env)工作(并且与上面相同),assign('x', 1, lst)但是不起作用.


1 为了详细说明,当然希望R使用动态调度(例如通过S3)对不同的对象类型做不同的事情.但是,这不是这种情况(至少不是直接):范围解析的区别发生在已知分配目标的对象类型之前 - 否则上述操作将在全局操作lst,而不是创建新的本地对象.所以内部[[<-必须做相当于:

`[[<-` = function (x, i, value) {
    if (exists(x, mode = 'environment', inherits = TRUE))
        assign(i, value, pos = x, inherits = FALSE)
    else if (exists(x, inherits = FALSE)
        internal_assign(x, i, value)
    else
        assign(x, list(i = value), pos = parent.frame(), inherits = FALSE)
}
Run Code Online (Sandbox Code Playgroud)

Rol*_*and 8

R-语言定义(第2.1.10)说:

与大多数其他R对象不同,环境在传递给函数或在赋值中使用时不会被复制.

"6.3关于评估的更多内容"部分也给出了一个略微相关的提示:

请注意,给定环境中的评估实际上可能会改变该环境,最明显的是涉及赋值运算符的情况,例如

eval(quote(total <- 0), environment(robert$balance)) # rob Rob
Run Code Online (Sandbox Code Playgroud)

在列表中进行评估时也是如此,但原始列表不会更改,因为一个人正在处理副本.

因此,您的第一个问题的答案是需要复制列表以分配给它们,但可以适当地修改环境(这具有巨大的性能影响).

关于你的第二个问题:

如果您正在使用列表,唯一的选择似乎是

  • 将列表复制到本地范围(使用get),
  • 分配到列表中,
  • 用于assign将修改后的列表复制回原始环境.