Не может изменять переменные в более высокой степени
Я часто хочу сделать следующее:
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()
.