分配以替换非本地列表中的值
Assignment to replace value in nonlocal list
[[<-
用于非本地对象时,列表和环境的行为不同:
lst = list()
env = new.env()
(function () lst[['x']] = 1)()
(function () env[['x']] = 1)()
lst
# list()
as.list(env)
# $x
# [1] 1
换句话说,如果 [[<-
的目标是环境,它会修改(非本地)环境,但如果它是 vector/list,它会创建一个新的本地对象。
我想知道两件事:
- 为什么会出现这种行为差异?
- 有没有一种方法可以让列表获得与环境相同的结果,不用 使用
<<-
?
关于 (1),我知道列表和环境之间存在多种差异(特别是,我知道环境不会被复制)但文档不会提及为什么 [[<-
的语义在两者之间不同——特别是,为什么运算符会在不同的范围内运行。这是一个错误吗?这至少是违反直觉的,并且需要一些不平凡的实施恶作剧。1
关于(2),显而易见的解决方案当然是使用<<-
:
(function () lst[['x']] <<- 1)()
但是,我更喜欢严格地理解差异,而不是仅仅解决它们。此外,到目前为止,我使用 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)
}
R-language definition(第 2.1.10 节)说:
Unlike most other R objects, environments are not copied when passed
to functions or used in assignments.
“6.3 更多评估”部分也给出了一个稍微相关的提示:
Notice that evaluation in a given environment may actually change that
environment, most obviously in cases involving the assignment
operator, such as
eval(quote(total <- 0), environment(robert$balance)) # rob Rob
This is also true when evaluating in lists, but the original list does
not change because one is really working on a copy.
因此,第一个问题的答案是需要复制列表以分配给它们,但可以就地修改环境(这对性能有巨大影响)。
关于你的第二个问题:
如果您正在使用列表,唯一的选择似乎是
- 将列表复制到本地范围(使用
get
),
- 分配到列表中,
- 使用
assign
将修改后的列表复制回原来的环境。
[[<-
用于非本地对象时,列表和环境的行为不同:
lst = list()
env = new.env()
(function () lst[['x']] = 1)()
(function () env[['x']] = 1)()
lst
# list()
as.list(env)
# $x
# [1] 1
换句话说,如果 [[<-
的目标是环境,它会修改(非本地)环境,但如果它是 vector/list,它会创建一个新的本地对象。
我想知道两件事:
- 为什么会出现这种行为差异?
- 有没有一种方法可以让列表获得与环境相同的结果,不用 使用
<<-
?
关于 (1),我知道列表和环境之间存在多种差异(特别是,我知道环境不会被复制)但文档不会提及为什么 [[<-
的语义在两者之间不同——特别是,为什么运算符会在不同的范围内运行。这是一个错误吗?这至少是违反直觉的,并且需要一些不平凡的实施恶作剧。1
关于(2),显而易见的解决方案当然是使用<<-
:
(function () lst[['x']] <<- 1)()
但是,我更喜欢严格地理解差异,而不是仅仅解决它们。此外,到目前为止,我使用 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)
}
R-language definition(第 2.1.10 节)说:
Unlike most other R objects, environments are not copied when passed to functions or used in assignments.
“6.3 更多评估”部分也给出了一个稍微相关的提示:
Notice that evaluation in a given environment may actually change that environment, most obviously in cases involving the assignment operator, such as
eval(quote(total <- 0), environment(robert$balance)) # rob Rob
This is also true when evaluating in lists, but the original list does not change because one is really working on a copy.
因此,第一个问题的答案是需要复制列表以分配给它们,但可以就地修改环境(这对性能有巨大影响)。
关于你的第二个问题:
如果您正在使用列表,唯一的选择似乎是
- 将列表复制到本地范围(使用
get
), - 分配到列表中,
- 使用
assign
将修改后的列表复制回原来的环境。