Объясните ленивую причуду оценки

Я читаю книгу Хэдли Уикхемса о 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
Другие вопросы по тегам