не могу изменить переменные в более высоком объеме - PullRequest
15 голосов
/ 17 апреля 2010

Я часто хочу сделать по существу следующее:

mat <- matrix(0,nrow=10,ncol=1)
lapply(1:10, function(i) { mat[i,] <- rnorm(1,mean=i)})

Но я ожидал бы, что в мате будет 10 случайных чисел, а скорее в нем 0. (Я не беспокоюсь о части rnorm. Очевидно, что есть правильный способ сделать это. Я беспокоюсь о влиянии на мат изнутри анонимной функции lapply) Могу ли я не влиять на матричный коврик изнутри lapply? Почему бы и нет? Есть ли ограничивающее правило для R, которое блокирует это?

Ответы [ 3 ]

28 голосов
/ 17 апреля 2010

Я обсуждал эту проблему в этом связанном вопросе: « Является ли семейство R больше, чем синтаксический сахар ».Вы заметите, что если вы посмотрите на сигнатуру функции для for и apply, у них есть одно критическое различие: цикл for оценивает выражение, а цикл apply оценивает функцию .

Если вы хотите изменить вещи вне области применения, вам нужно использовать <<- или assign.Или, более конкретно, используйте вместо этого что-то вроде цикла for.Но вам действительно нужно быть осторожным при работе с вещами вне функции, потому что это может привести к неожиданному поведению.

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

Наконец, правильный способ запустить пример кода - это также передать параметры вашей функции следующим образом и присвоить результат:

mat <- matrix(0,nrow=10,ncol=1)
mat <- matrix(lapply(1:10, function(i, mat) { mat[i,] <- rnorm(1,mean=i)}, mat=mat))

Всегда лучше бытьявно о параметре, когда это возможно (следовательно, mat=mat), а не выводить его.

6 голосов
/ 17 апреля 2010

Одним из основных преимуществ функций более высокого порядка, таких как lapply() или sapply(), является то, что вам не нужно инициализировать свой «контейнер» (в данном случае матрицу).

Как Фойтасек предлагает:

as.matrix(lapply(1:10,function(i) rnorm(1,mean=i)))

В качестве альтернативы:

do.call(rbind,lapply(1:10,function(i) rnorm(1,mean=i)))

Или просто как числовой вектор:

sapply(1:10,function(i) rnorm(1,mean=i))

Если вы действительно хотите изменить указанную выше переменную области действия вашей анонимной функции (генератор случайных чисел в данном случае), используйте <<-

> mat <- matrix(0,nrow=10,ncol=1)
> invisible(lapply(1:10, function(i) { mat[i,] <<- rnorm(1,mean=i)}))
> mat
           [,1]
 [1,] 1.6780866
 [2,] 0.8591515
 [3,] 2.2693493
 [4,] 2.6093988
 [5,] 6.6216346
 [6,] 5.3469690
 [7,] 7.3558518
 [8,] 8.3354715
 [9,] 9.5993111
[10,] 7.7545249

См. этот пост о <<-. Но в этом конкретном примере цикл for будет иметь больше смысла:

mat <- matrix(0,nrow=10,ncol=1)
for( i in 1:10 ) mat[i,] <- rnorm(1,mean=i)

с незначительными затратами на создание индексной переменной i в глобальной рабочей области.

1 голос
/ 17 апреля 2010

Вместо фактического изменения mat, lapply просто возвращает измененную версию mat (в виде списка).Вам просто нужно присвоить его мату и превратить обратно в матрицу, используя as.matrix().

...