Не может изменять переменные в более высокой степени

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

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

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

Ответ 1

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

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

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

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

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), а не выводить его.

Ответ 2

Одним из основных преимуществ функций более высокого порядка, таких как 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-loop будет иметь смысл:

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

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

Ответ 3

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