[[<-
ведет себя по-разному для списков и сред при использовании на нелокальных объектах:
lst = list()
env = new.env()
(function () lst[['x']] = 1)()
(function () env[['x']] = 1)()
lst
# list()
as.list(env)
# $x
# [1] 1
Другими словами, если целью [[<-
является среда, она изменяет (нелокальную) среду, но если ее вектор/список создает новый локальный объект.
Я хотел бы знать две вещи:
- Почему это различие в поведении?
- Есть ли способ добиться того же результата для списков, что и для сред, без использования
<<-
?
Что касается (1), мне известно о множественных различиях между списками и средами (в частности, я знаю, что среды не копируются), но в документации не упоминается, почему семантика [[<-
отличается между двумя - в частности, почему оператор будет работать в разных сферах. Это ошибка? Его контринтуитивный, по крайней мере, и требует некоторых нетривиальных махинаций реализации. 1
Что касается (2), очевидное решение, конечно, должно использовать <<-
:
(function () lst[['x']] <<- 1)()
Однако я предпочитаю понимать разницу строго, а не просто обойти их. Кроме того, Ive до сих пор использовал assign
вместо <<-
, и я предпочитаю это, так как он позволяет мне больше контролировать область назначения (в частности, поскольку я могу указать inherits = FALSE
. <<-
слишком много voodoo для мой вкус.
Однако вышеупомянутое не может быть разрешено (насколько мне известно) с помощью 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)
}