Я только что закончил читать об области видимости во введении R , и мне очень интересно узнать о <<-
назначении.
В руководстве приведен один (очень интересный) пример <<-
, который, как мне кажется, я понял. Мне все еще не хватает контекста, когда это может быть полезно.
Так что я бы хотел прочитать от вас примеры (или ссылки на примеры) того, когда использование <<-
может быть интересным / полезным. В чем могут быть опасности его использования (кажется, это легко не заметить) и какие советы вы можете захотеть поделиться.
r
scoping
lexical-scope
r-faq
Тал Галили
источник
источник
<<-
для сохранения ключевых переменных, сгенерированных внутри функции, для записи в журналы отказов при сбое функции. Может помочь сделать сбой воспроизводимым, если функция использует входные данные (например, из внешних API), которые не обязательно были бы сохранены в противном случае из-за сбоя.Ответы:
<<-
наиболее полезен в сочетании с замыканиями для поддержания состояния. Вот отрывок из моей недавней статьи:Замыкание - это функция, написанная другой функцией. Замыкания называются так, потому что они охватывают среду родительской функции и могут получить доступ ко всем переменным и параметрам в этой функции. Это полезно, потому что позволяет нам иметь два уровня параметров. Один уровень параметров (родительский) контролирует работу функции. Другой уровень (ребенок) выполняет работу. В следующем примере показано, как можно использовать эту идею для создания семейства степенных функций. Родительская функция (
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
источник
Это помогает думать об этом
<<-
как об эквивалентеassign
(если вы установите дляinherits
параметра в этой функции значениеTRUE
). Преимуществоassign
заключается в том, что он позволяет вам указывать больше параметров (например, среду), поэтому в большинстве случаев я предпочитаю использоватьassign
over<<-
.Использование
<<-
иassign(x, value, inherits=TRUE)
означает, что «окружающие среды предоставленной среды ищутся до тех пор, пока не встретится переменная 'x'». Другими словами, он будет продолжать просматривать среды по порядку, пока не найдет переменную с таким именем и не присвоит ее ей. Это может быть в рамках функции или в глобальной среде.Чтобы понять, что делают эти функции, вам нужно также понимать среды R (например, использование
search
).Я регулярно использую эти функции, когда запускаю большое моделирование и хочу сохранить промежуточные результаты. Это позволяет вам создавать объект вне области действия данной функции или
apply
цикла. Это очень полезно, особенно если вас беспокоит неожиданное завершение большого цикла (например, отключение базы данных), и в этом случае вы можете потерять все в процессе. Это было бы эквивалентно записи ваших результатов в базу данных или файл во время длительного процесса, за исключением того, что вместо этого результаты сохраняются в среде R.Мое главное предупреждение: будьте осторожны, потому что теперь вы работаете с глобальными переменными, особенно при использовании
<<-
. Это означает, что вы можете столкнуться с ситуациями, когда функция использует значение объекта из среды, когда вы ожидали, что она будет использовать значение, указанное в качестве параметра. Это одна из основных вещей, которых пытается избежать функциональное программирование (см. Побочные эффекты ). Я избегаю этой проблемы, присваивая свои значения уникальным именам переменных (с использованием вставки с заданными или уникальными параметрами), которые никогда не используются внутри функции, а используются только для кеширования и в случае, если мне нужно будет восстановить позже (или выполнить некоторые мета -анализ промежуточных результатов).источник
Одно место, где я использовал,
<<-
было в простых графических интерфейсах с использованием tcl / tk. В некоторых из начальных примеров он есть - так как вам необходимо различать локальные и глобальные переменные для сохранения состояния. См. Напримерlibrary(tcltk) demo(tkdensity)
который использует
<<-
. В остальном я согласен с Мареком :) - поиск в Google может помочь.источник
tkdensity
в R 3.6.0.f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())} plot(f(1000,0),typ="l")
источник
<<-
. В этом случае цикл for будет более понятным.По этому поводу я хотел бы отметить, что
<<-
оператор будет вести себя странно при применении (неправильно) в цикле 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.Надеюсь, это кому-то поможет - сегодня это поставило меня в тупик на пару часов! (Кстати, просто замените
<<-
на,<-
и функция будет работать должным образом).источник
mySum
никогда не увеличивается, а только глобальныйmySum
. Следовательно, на каждой итерации цикла for значение globalmySum
получает значение0 + i
. Вы можете следить за этим с помощьюdebug(fortest)
.<-
везде последовательно внутри функции, если вы хотите обновить только локальную переменную внутри функции.<<-
Оператор также может быть полезен для ссылочных классов при написании эталонных методов . Например: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
источник