Можете ли вы более четко объяснить ленивую оценку в операторах 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.