2010-04-13 4 views
96

Я только что закончил читать около scoping in the R intro, и мне очень интересно узнать о назначении <<-.Как вы используете «<< -» (назначение области видимости) в R?

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

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

ответ

139

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

Закрытие - это функция, написанная другой функцией.Закрытия так называются, потому что они заключают в себе среду родительской функции и могут получить доступ ко всем переменным и параметрам в этой функции. Это полезно, потому что это позволяет нам иметь два уровня параметров. Один уровень параметров (родительский) определяет, как работает эта функция. Другой уровень (ребенок) выполняет эту работу. В следующем примере показано, как использовать эту идею для генерации семейства силовых функций. Родительская функция (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

Эй, это нерешенная задача R на Rosettacode (http://rosettacode.org/wiki/Accumulator_factory#R) Ну, это было ... –

+0

Было бы нужно заключить более 1 замыкания в одну родительскую функцию? Я просто попробовал один фрагмент, кажется, что только последнее закрытие было выполнено ... –

5

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

library(tcltk) 
demo(tkdensity) 

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

5
f <- function(n, x0) {x <- x0; replicate(n, (function(){x <<- x+rnorm(1)})())} 
plot(f(1000,0),typ="l") 
+7

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

25

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

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

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

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

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

+2

Спасибо Тал. У меня есть блог, хотя я действительно не использую его. Я никогда не могу закончить запись, потому что я не хочу ничего публиковать, если это не идеально, и у меня просто нет времени для этого ... – Shane

+1

Мудрый человек однажды сказал мне, что не важно быть совершенным - только стоять - что вы есть, и так будут ваши посты. Также - иногда читатели помогают улучшить текст комментариями (это то, что происходит с моим блогом). Надеюсь, в один прекрасный день вы передумаете :) –

2

По этому вопросу я хотел бы отметить, что < < оператор будет вести себя странно, когда применяется (неправильно) в пределах для цикла (может быть других случаях). Учитывая следующий код:

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, просто замените < < на <, и функция работает должным образом).

+2

в вашем примере локальный 'mySum'is никогда не увеличивался, а только глобальный' mySum'. Следовательно, на каждой итерации цикла for глобальное 'mySum' получает значение' 0 + i'. Вы можете следовать этому с помощью 'debug (fortest)'. – clemlaflemme

+0

Это не имеет никакого отношения к тому, что это цикл for; вы ссылаетесь на две разные области. Просто используйте '<-' всюду внутри функции, если вы хотите обновить локальную переменную внутри функции. – smci

+0

Или используйте << - везде @smci. Хотя лучше всего избегать глобалов. –

2

Оператор <<- также может быть полезен для Reference Classes when writing Reference Methods. Например:

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 

 Смежные вопросы

  • Нет связанных вопросов^_^