Можете ли вы более четко объяснить ленивую оценку в операторах R-функций?

Если я создаю функцию следующим образом:

what_is_love <- function(f) {
  function(...) {
    cat('f is', f, '\n')
  }
}

И позвони с lapply: funs <- lapply(c('love', 'cherry'), what_is_love)

Я получаю неожиданный вывод:

> funs[[1]]()
f is cherry
> funs[[2]]()
f is cherry

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

> f1 <- what_is_love('love')
> f2 <- what_is_love('cherry')
> f1()
f is love
> f2()
f is cherry

Что дает?

я знаю это funs <- lapply(c('love', 'cherry'), what_is_love) можно выписать более полно:

params <- c('love', 'cherry')
out <- vector('list', length(params))
for (i in seq_along(params)) {
  out[[i]] <- what_is_love(params[[i]])
}
out

Но когда я захожу, я вижу, что обе функции имеют свою собственную среду:

Browse[1]> out[[1]]
function(...) {
    cat('f is', f, '\n')
  }
<environment: 0x109508478>
Browse[1]> out[[2]]
function(...) {
    cat('f is', f, '\n')
  }
<environment: 0x1094ff750>

Но в каждой из этих сред, f та же...

Browse[1]> environment(out[[1]])$f
[1] "cherry"
Browse[1]> environment(out[[2]])$f
[1] "cherry"

Я знаю, что ответ "ленивая оценка", но я ищу немного больше глубины... как f в конечном итоге переназначается в обеих средах? Где же f родом из? Как ленивая оценка работает в этом примере?

-

РЕДАКТИРОВАТЬ: я знаю о другом вопросе о ленивой оценки и функционалов, но он просто говорит, что ответ "ленивая оценка" без объяснения того, как на самом деле работает ленивая оценка. Я ищу большую глубину.

1 ответ

Решение

Когда вы делаете

what_is_love <- function(f) {
  function(...) {
    cat('f is', f, '\n')
  }
}

внутренняя функция создает оболочку для f, но подвох в том, что до тех пор, пока вы фактически не используете переменную, переданную функции, она остается "обещанием" и фактически не оценивается. Если вы хотите "захватить" текущее значение f тогда вам нужно форсировать оценку обещания; Вы можете использовать force() Функция для этого.

what_is_love <- function(f) {
  force(f)
  function(...) {
    cat('f is', f, '\n')
  }
}
funs <- lapply(c('love', 'cherry'), what_is_love)

funs[[1]]()
# f is love 
funs[[2]]()
# f is cherry 

Без force(), f остается обещанием внутри обеих функций в вашем списке. Он не оценивается до тех пор, пока вы не вызовете функцию, а при вызове функции это обещание оценивается до последнего известного значения для f который "вишня".

Как отметил @MartinMorgran, это поведение изменилось в R 3.2.0. Из заметок о выпуске

Функции более высокого порядка, такие как функции apply и Reduce(), теперь передают аргументы функциям, которые они применяют, чтобы исключить нежелательные взаимодействия между отложенной оценкой и захватом переменных в замыканиях. Это решает PR#16093.

Другие вопросы по тегам