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


Я читаю книгу Хэдли Уикхэмс о 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 58

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