Объясните ленивую оценочную причуду
Я читаю книгу Хэдли Уикхэма на Github, в частности эту часть на ленивую оценку. Там он приводит пример последствий ленивой оценки в части с функциями add/adders
. Позвольте мне процитировать этот бит:
Эта [ленивая оценка] важна при создании замыканий с помощью лаппеля или цикла:
add <- function(x) {
function(y) x + y
}
adders <- lapply(1:10, add)
adders[[1]](10)
adders[[10]](10)
x лениво оценивается при первом вызове одного из сумматоров функции. На этом этапе цикл завершен, и конечное значение x равно 10. Поэтому все функции сумматора добавят 10 к их вход, возможно, не то, что вы хотели! Вручную принудительное исправление проблема:
add <- function(x) {
force(x)
function(y) x + y
}
adders2 <- lapply(1:10, add)
adders2[[1]](10)
adders2[[10]](10)
Кажется, я не понимаю этот бит, и объяснение там минимальное. Может ли кто-нибудь прокомментировать конкретный пример и объяснить, что там происходит? Я определенно озадачен предложением "в этот момент цикл завершен, а конечное значение x равно 10". Какой цикл? Какое окончательное значение, где? Должно быть, что-то простое, что мне не хватает, но я просто этого не вижу. Большое спасибо заранее.
Ответы
Ответ 1
Цель:
adders <- lapply(1:10, function(x) add(x) )
заключается в создании списка функций add
, первый добавляет 1 к его входу, второй добавляет 2 и т.д. Ленивая оценка заставляет R ждать действительно создания функций сумматоров, пока вы действительно не начнете вызывать функции. Проблема в том, что после создания первой функции сумматора x
увеличивается на цикл lapply
, заканчивающийся на значение 10. Когда вы вызываете первую функцию сумматора, ленивая оценка теперь строит функцию, получая значение x
. Проблема в том, что исходный x
больше не равен единице, а соответствует значению в конце цикла lapply
, т.е. 10.
Следовательно, ленивая оценка заставляет все функции сумматора ждать до тех пор, пока цикл lapply
не завершится при действительно построении функции. Затем они строят свою функцию с одинаковым значением, т.е. 10. Решение, которое предлагает Хэдли, заключается в том, чтобы заставить x
оцениваться напрямую, избегая ленивой оценки и получая правильные функции с правильными значениями x
.
Ответ 2
Это уже не так, как в R 3.2.0!
Соответствующая строка в журнале изменений читает:
Функции более высокого порядка, такие как применяемые функции и Reduce() теперь аргументы силы в отношении функций, которые они применяют для устранения нежелательные взаимодействия между ленивой оценкой и переменным захватом в закрытии.
И действительно:
add <- function(x) {
function(y) x + y
}
adders <- lapply(1:10, add)
adders[[1]](10)
# [1] 11
adders[[10]](10)
# [1] 20