Как вы используете ‹< - (задание области видимости) в R?

Я только что закончил читать о области видимости во введении R, и мне очень интересно узнать о назначении <<-.

В руководстве приведен один (очень интересный) пример для <<-, который, как мне кажется, я понял. Мне все еще не хватает контекста, когда это может быть полезно.

Я хотел бы прочитать от вас примеры (или ссылки на примеры) того, когда использование <<- может быть интересным / полезным. В чем могут быть опасности его использования (кажется, это легко не заметить) и какие советы вы можете захотеть поделиться.


person Tal Galili    schedule 13.04.2010    source источник
comment
Я использовал <<- , чтобы сохранить ключевые переменные, сгенерированные внутри функции, для записи в журналы отказов при сбое функции. Может помочь сделать сбой воспроизводимым, если функция использовала входные данные (например, из внешних API-интерфейсов), которые не обязательно были бы сохранены в противном случае из-за сбоя.   -  person geotheory    schedule 09.10.2020


Ответы (6)


<<- наиболее полезен в сочетании с замыканиями для поддержания состояния. Вот отрывок из моей недавней статьи:

Замыкание - это функция, написанная другой функцией. Замыкания называются так, потому что они охватывают среду родительской функции и могут получить доступ ко всем переменным и параметрам в этой функции. Это полезно, потому что позволяет нам иметь два уровня параметров. Один уровень параметров (родительский) контролирует работу функции. Другой уровень (ребенок) выполняет работу. В следующем примере показано, как можно использовать эту идею для создания семейства степенных функций. Родительская функция (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
person hadley    schedule 13.04.2010
comment
Привет, это нерешенная задача R в Rosettacode (rosettacode.org/wiki/Accumulator_factory#R) Ну это было ... - person Karsten W.; 15.04.2010
comment
Будет ли необходимость заключать более 1 замыкания в одну родительскую функцию? Я пробовал только один фрагмент, кажется, было выполнено только последнее закрытие ... - person mckf111; 03.02.2018
comment
Есть ли какая-либо альтернатива знаку равенства знаку ‹< -? - person Saren Tasciyan; 01.05.2019

Это помогает думать о <<- как о эквиваленте assign (если вы установите параметр inherits в этой функции на TRUE). Преимущество assign в том, что он позволяет вам указать больше параметров (например, среду), поэтому в большинстве случаев я предпочитаю использовать assign вместо <<-.

Использование <<- и assign(x, value, inherits=TRUE) означает, что «окружающие среды предоставленной среды ищутся до тех пор, пока не встретится переменная 'x'». Другими словами, он будет продолжать просматривать среды по порядку, пока не найдет переменную с таким именем и не присвоит ее ей. Это может быть в рамках функции или в глобальной среде.

Чтобы понять, что делают эти функции, вам также необходимо разбираться в среде R (например, с использованием search).

Я регулярно использую эти функции, когда запускаю большое моделирование и хочу сохранить промежуточные результаты. Это позволяет вам создавать объект вне области действия данной функции или apply цикла. Это очень полезно, особенно если вас беспокоит неожиданное завершение большого цикла (например, отключение базы данных), и в этом случае вы можете потерять все в процессе. Это было бы эквивалентно записи результатов в базу данных или файл во время длительного процесса, за исключением того, что вместо этого результаты сохраняются в среде R.

Мое главное предупреждение: будьте осторожны, потому что теперь вы работаете с глобальными переменными, особенно при использовании <<-. Это означает, что вы можете столкнуться с ситуациями, когда функция использует значение объекта из среды, когда вы ожидали, что она будет использовать значение, указанное в качестве параметра. Это одна из основных вещей, которых функциональное программирование пытается избежать (см. побочные эффекты ). Я избегаю этой проблемы, присваивая свои значения уникальным именам переменных (с использованием вставки с заданными или уникальными параметрами), которые никогда не используются в функции, а используются только для кеширования, и в случае, если мне нужно будет восстановить позже (или выполнить некоторые мета -анализ промежуточных результатов).

person Shane    schedule 13.04.2010
comment
Спасибо, Тал. У меня есть блог, хотя я им особо не пользуюсь. Я никогда не смогу закончить пост, потому что я не хочу публиковать ничего, кроме идеального, и у меня просто нет на это времени ... - person Shane; 13.04.2010
comment
Один мудрый человек однажды сказал мне, что не важно быть безупречным - важно только выделяться, - что вы и есть, и ваши посты будут такими же. Также - иногда читатели помогают улучшить текст комментариями (вот что происходит с моим блогом). Надеюсь, однажды ты передумаешь :) - person Tal Galili; 13.04.2010

Одно место, где я использовал <<-, было в простых графических интерфейсах с использованием tcl / tk. В некоторых из начальных примеров он есть - так как вам нужно различать локальные и глобальные переменные для сохранения состояния. См. Например

 library(tcltk)
 demo(tkdensity)

который использует <<-. В остальном я согласен с Мареком :) - поиск в Google может помочь.

person Dirk Eddelbuettel    schedule 13.04.2010
comment
Интересно, что я как-то не могу найти tkdensity в R 3.6.0. - person NelsonGon; 26.06.2019
comment
Пакет tcltk поставляется с R: github .com / wch / r-source / blob / trunk / src / library / tcltk / demo /. - person Dirk Eddelbuettel; 26.06.2019

По этому поводу я хотел бы отметить, что оператор <<- будет вести себя странно при применении (неправильно) в цикле 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.

Надеюсь, это кому-то поможет - сегодня это поставило меня в тупик на пару часов! (Кстати, просто замените <<- на <-, и функция будет работать должным образом).

person Matthew Wise    schedule 15.06.2015
comment
в вашем примере локальный mySum никогда не увеличивается, а только глобальный mySum. Следовательно, на каждой итерации цикла for глобальный mySum получает значение 0 + i. Вы можете следить за этим с помощью debug(fortest). - person ClementWalter; 26.10.2015
comment
Это не имеет ничего общего с тем, что это цикл for; вы ссылаетесь на две разные области видимости. Просто используйте <- везде последовательно внутри функции, если вы хотите обновить только локальную переменную внутри функции. - person smci; 29.04.2016
comment
Или используйте ‹< - везде @smci. Хотя лучше избегать глобалов. - person Union find; 05.12.2017

Оператор <<- также может быть полезен для эталонных классов, когда написание справочных методов. Например:

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
person Carlos Cinelli    schedule 15.06.2015

person    schedule
comment
Это хороший пример того, где нельзя использовать <<-. В этом случае цикл for будет более понятным. - person hadley; 13.04.2010