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