Как вы используете "<< -" (присвоение области охвата) в R?
Я только что закончил читать область действия в R intro, и мне очень любопытно присвоить <<-
.
В руководстве был показан один (очень интересный) пример для <<-
, который, как я чувствую, я понял. То, что мне до сих пор не хватает, - это контекст того, когда это может быть полезно.
Так что я хотел бы прочитать от вас примеры (или ссылки на примеры), когда использование <<-
может быть интересным/полезным. Какая может быть опасность его использования (он легко отслеживается) и любые советы, которые могут вам понравиться.
Ответы
Ответ 1
<<-
наиболее полезен в сочетании с закрытием для поддержания состояния. Вот раздел из недавней моей статьи:
Замыкание - это функция, написанная другой функцией. Закрытия так называются, поскольку они охватывают среду родительской функции и могут обращаться ко всем переменным и параметрам в этой функции. Это полезно, потому что это позволяет нам иметь два уровня параметров. Один уровень параметров (родительский) определяет, как работает эта функция. Другой уровень (ребенок) выполняет эту работу. В следующем примере показано, как использовать эту идею для генерации семейства силовых функций. Родительская функция (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
Ответ 2
Это помогает думать о <<-
как эквивалент assign
(если вы установите для параметра inherits
в этой функции значение TRUE
). Преимущество assign
заключается в том, что он позволяет указать больше параметров (например, окружение), поэтому я предпочитаю использовать assign
более <<-
в большинстве случаев.
Использование <<-
и assign(x, value, inherits=TRUE)
означает, что "окружение среды поставляемой среды выполняется до тех пор, пока не встретится переменная" x ". Другими словами, он будет продолжать перемещаться по средам, пока не найдет переменную с этим именем, и она назначит ей это. Это может быть в пределах функции или в глобальной среде.
Чтобы понять, что делают эти функции, вам также нужно понять среды R (например, используя search
).
Я регулярно использую эти функции, когда выполняю большую симуляцию, и хочу сохранить промежуточные результаты. Это позволяет вам создать объект за пределами области действия данной функции или цикла apply
. Это очень полезно, особенно если у вас есть какая-либо озабоченность по поводу большого цикла, заканчивающегося неожиданно (например, отключение базы данных), и в этом случае вы можете потерять все в этом процессе. Это было бы эквивалентно написанию ваших результатов в базе данных или файле во время продолжительного процесса, за исключением того, что вместо этого они сохраняли результаты в среде R.
Мое основное предупреждение с этим: будьте осторожны, потому что теперь вы работаете с глобальными переменными, особенно при использовании <<-
. Это означает, что вы можете столкнуться с ситуациями, когда функция использует значение объекта из среды, когда вы ожидали, что она будет использовать тот, который был поставлен в качестве параметра. Это одна из основных вещей, которую пытается избежать функциональное программирование (см. побочные эффекты). Я избегаю этой проблемы, назначая мои значения уникальным именам переменных (используя пасту с набором или уникальными параметрами), которые никогда не используются в функции, а просто используются для кэширования, и в случае, если мне нужно позже восстановить (или сделать некоторые мета -анализ на промежуточные результаты).
Ответ 3
Одно место, где я использовал <<-
, было в простых графических интерфейсах, используя tcl/tk. Некоторые из исходных примеров имеют это, так как вам нужно провести различие между локальными и глобальными переменными для обеспечения полноты. См. Например
library(tcltk)
demo(tkdensity)
который использует <<-
. В противном случае я согласен с Marek:) - поиск Google может помочь.
Ответ 4
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())}
plot(f(1000,0),typ="l")
Ответ 5
По этому вопросу я хотел бы указать, что < оператор будет вести себя странно при применении (некорректно) в цикле 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 при выходе из функции.
Надеюсь, это поможет кому-то - это заставило меня на пару часов сегодня! (BTW, просто замените < < с < и функция работает как ожидалось).
Ответ 6
Оператор <<-
также может быть полезен для Reference Classes при написании справочных методов. Например:
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