Вопрос или проблема
Проблема
Есть случай, как этот,
acc = \ (a) \ (b) base::list(a = a, b = b)
for (x in c("A","B")) {acc = acc(x)}
acc
Он возвращает:
$a
[1] "B"
$b
[1] "B"
Но он должен возвращать то же самое, что и,
acc = \ (a) \ (b) base::list(a = a, b = b)
# for (x in c("A","B")) {acc = acc(x)}
acc = acc("A")
acc = acc("B")
acc
То есть он должен возвращать:
$a
[1] "A"
$b
[1] "B"
Больше случаев
acc = \ (x) \ (y) \ (z) base::list(x = x, y = y, z = z)
for (i in c("A","B","C")) {acc = acc(i)}
acc
Он возвращает:
$x
[1] "C"
$y
[1] "C"
$z
[1] "C"
Но он должен возвращать то же самое с
acc = \ (x) \ (y) \ (z) base::list(x = x, y = y, z = z)
# for (i in c("A","B","C")) {acc = acc(i)}
acc = acc("A")
acc = acc("B")
acc = acc("C")
acc
То есть он должен возвращать:
$x
[1] "A"
$y
[1] "B"
$z
[1] "C"
И та же проблема с циклом while…
acc = \ (a) \ (b) base::list(a = a, b = b)
.iter = c("A","B")
.i = 0
while (.i < length(.iter)) {acc <<- acc <- acc(.iter[.i + 1]); .i = .i + 1}
print(acc)
Итак…
Какая причина в R языке привела к этой проблеме?
Я протестировал это как в R-4.3.3, так и в webr REPL. Вы можете просто запустить это здесь.
Ответ или решение
Проблема с циклом for и функцией acc в языке R
Описание проблемы
В вашем вопросе представлена интересная ситуация, касающаяся работы с функцией-аккумулятором (acc
) в циклах for
и while
в языке программирования R. Проблема заключается в том, что при использовании acc
в цикле результатов не соответствует ожидаемому выводу. Рассмотрим подробности:
acc = \(a)(b) base::list(a = a, b = b)
for (x in c("A", "B")) { acc = acc(x) }
В этом случае вывод будет:
$a
[1] "B"
$b
[1] "B"
Однако, если мы заменим цикл for
на последовательные вызовы функции:
acc = acc("A")
acc = acc("B")
То ожидаемым выводом будет:
$a
[1] "A"
$b
[1] "B"
Причины возникновения проблемы
Причина, по которой цикл for
не дает ожидаемого результата, заключается в том, как работает функция высшего порядка и как она взаимодействует с аргументами. Каждый раз, когда acc
вызывается, она возвращает новую структуру данных, получая в качестве аргументов текущие значения. Однако при использовании цикла for
итоговый результат сохраняется в переменной acc
, что приводит к тому, что накапливается не список аргументов, а значения последнего вызова.
-
Функции высшего порядка: В R переменные, используемые для передачи аргументов, обрабатываются по ссылке. Когда
acc
вызывается в цикле, ее результат перезаписывает переменнуюacc
, и когда цикл подходит к концу, вы получаете результат, который представляет только последний аргумент, переданный вacc
. -
Логика цикл vs. последовательные вызовы: В цикле
for
каждый вызовacc(x)
заменяет предыдущее значение, тогда как в серийных вызовах происходит накапливание, что и дает желаемый эффект.
Пример с циклом while
Вы также упомянули использование цикла while
:
.iter = c("A", "B")
.i = 0
while (.i < length(.iter)) {
acc <- acc(.iter[.i + 1])
.i = .i + 1
}
Этот цикл также выдает неправильный результат, аналогично циклу for
, поскольку логика перезаписи остается той же. Результат будет таким же, как и в случае с циклом for
, где acc
будет содержать только последний элемент.
Итоговое замечание
Основная проблема заключается в необходимости понимать, как работает замыкание и передача аргументов в R. Для корректной работы с функциями-аккумуляторами важно следить за тем, как осуществляется сбор значений во время выполнения циклов. Вместо перезаписи acc
следует сохранять промежуточные результаты, если требуется накапливать значения.
Для правильного ведения накопления используйте разные методы, например, создание нового списка или векторов в каждом шаге или использование Reduce()
для применения функции к спискам.
Заключение
Меняйте подход к использованию функции-аккумулятора в цикле и помните о том, что при каждом вызове функции происходят изменения в значениях переменных. Научитесь контролировать поток данных через вашу функцию, чтобы избежать подобных ошибок в будущем.