R – Цикл for может иметь ошибку при работе с acc как с функцией. Но почему?

Вопрос или проблема

Проблема

Есть случай, как этот,

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, что приводит к тому, что накапливается не список аргументов, а значения последнего вызова.

  1. Функции высшего порядка: В R переменные, используемые для передачи аргументов, обрабатываются по ссылке. Когда acc вызывается в цикле, ее результат перезаписывает переменную acc, и когда цикл подходит к концу, вы получаете результат, который представляет только последний аргумент, переданный в acc.

  2. Логика цикл 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() для применения функции к спискам.

Заключение

Меняйте подход к использованию функции-аккумулятора в цикле и помните о том, что при каждом вызове функции происходят изменения в значениях переменных. Научитесь контролировать поток данных через вашу функцию, чтобы избежать подобных ошибок в будущем.

Оцените материал
Добавить комментарий

Капча загружается...