Как вы используете "<< -" (присвоение области охвата) в 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