Подтвердить что ты не робот

Странное поведение Mapply: что я пропустил?

Следующий код не работает, как я ожидал:

a <- list(0, 1)
b <- list(0, 1)

# return a linear function with slope `a` and intercept `b`.
f <- function(a, b) function(x) a*x + b

# create a list of functions with different parameters.
fs <- mapply(f, a, b)

# test
fs[[1]](3)
# [1] 4  # expected zero!
fs[[2]](3)
# [1] 4

Может ли кто-нибудь сказать мне, почему?

NB: Я нашел обходное решение, поэтому я не ищу другого способа достижения желаемого результата. Но мне любопытно, почему этот конкретный подход не сработал.


Update:

Начиная с R 3.2.0, теперь это работает так, как ожидалось:

a <- list(0, 1)
b <- list(0, 1)
f <- function(a, b) function(x) a*x + b
fs <- mapply(f, a, b)

# test
fs[[1]](3)
# [1] 0 
fs[[2]](3)
# [1] 4
4b9b3361

Ответ 1

[Обновить] Мой первоначальный анализ был правильным, но выводы были неправильными:) Пусть дойдет до выводов после анализа.

Здесь показан код, демонстрирующий эффекты:

x <- lapply(1:3, function(x) sys.frame(sys.nframe()))
x[[1]] # An environment
x[[2]] # Another environment
x[[3]] # Yet nother environment
x[[1]]$x  # 3!!! (should be 1)
x[[2]]$x  # 3!!  (should be 2)
x[[3]]$x  # 3 as expected

# Accessing the variable within the function will "fix" the weird behavior:
x <- lapply(1:3, function(x) {x; sys.frame(sys.nframe())})
x[[1]]$x  # 1
x[[2]]$x  # 2
x[[3]]$x  # 3

Итак, обход в вашем случае:

f <- function(a, b) { a;b; function(x) a*x + b }

Btw, как отмечает @James, есть функция force, которая делает доступ к переменной более явной:

f <- function(a, b) { force(a);force(b); function(x) a*x + b }

Выводы

Ну, как заметили @mbq и @hadley, это связано с ленивой оценкой. Легче показать с помощью простой петли:

fs <- list(); for(i in 1:2) fs[[i]] <- f(a[[i]], b[[i]])

Аргумент f x функции не получает значение a[[i]] (которое равно 0), но все выражение и среда, где a и i существуют. Когда вы обращаетесь к x, он получает оценку и поэтому использует i во время оценки. Если for-loop перемещается с момента вызова на f, вы получаете "неправильный" результат...

Первоначально я сказал, что это произошло из-за ошибки в *apply, чего нет.... но поскольку я ненавижу ошибаться, я могу указать, что * apply У DOES есть ошибка (или, возможно, больше несогласованности) в этих случаях:

lapply(11:12, function(x) sys.call())
#[[1]]
#FUN(11:12[[1L]], ...)
#
#[[2]]
#FUN(11:12[[2L]], ...)

lapply(11:12, function(x) function() x)[[1]]() # 12
lapply(11:12, function(x) function() x)[[2]]() # 12

Как вы видите выше, код lapply говорит, что он вызывает функцию с 11:12[[1L]]. Если вы оцениваете, что "позже" вы все равно получите значение 11, но вы действительно получите 12!

Это, вероятно, связано с тем, что lapply реализован в коде C по причинам производительности и обманывает бит, поэтому выражение, которое оно показывает, не является выражением, которое оценивается - ergo, ошибка...

КЭД

Ответ 2

Это результат ленивой оценки - все аргументы передаются по дереву вызовов как promises, чтобы избежать ненужного выполнения и оставаться в этом приостановленном состоянии, пока R не убедится в том, что они используются.

В вашем коде вы просто заполняете функции с тем же обещанием и тем же обещанием b; тогда все они были преданы последней паре валов. Как уже заметил @Tommy, решение состоит в том, чтобы принудительно использовать "использование" значения до того, как функция будет определена.