Генератор — это функция, которая производит потенциально бесконечный поток значений. Каждый раз, когда функция производит значение, она приостанавливает свою работу и ожидает, пока вызывающая сторона запросит следующее значение.
iter-defun определяет генераторную функцию. Генераторная функция
имеет ту же сигнатуру, что и обычная функция, но работает по-другому. Вместо
выполнения body при вызове генераторная функция возвращает
объект итератора. Этот итератор выполняет body для генерации
значений, выдавая значение и приостанавливаясь в месте, где встречается
iter-yield или iter-yield-from. Когда body
завершается нормально, iter-next сигнализирует iter-end-of-sequence
с результатом body в качестве данных условия.
Любой код Lisp допустим внутри body, но
iter-yield и iter-yield-from не могут встречаться
в формах unwind-protect.
iter-lambda создает неономерованную генераторную функцию,
которая работает точно так же, как генераторная функция, созданная с помощью
iter-defun.
Когда он встречается внутри генераторной функции, iter-yield
указывает на то, что текущий итератор должен приостановиться и вернуть
value из iter-next. iter-yield
эквивалентен параметру value в следующем вызове
iter-next.
iter-yield-from выдает все значения, которые производит
iterator, и эквивалентен значению, которое возвращает
генераторная функция iterator при нормальном завершении. Пока
он контролирует выполнение, iterator получает значения,
отправленные ему с помощью iter-next.
Чтобы использовать генераторную функцию, сначала вызовите ее обычным образом,
получив объект итератора. Итератор — это конкретный экземпляр
генератора. Затем используйте iter-next для извлечения
значений из этого итератора. Когда больше значений для извлечения из
итератора нет, iter-next вызывает условие
iter-end-of-sequence с конечным значением итератора.
Важно отметить, что тело генераторной функции выполняется только внутри вызовов
iter-next. Вызов функции, определенной с помощью
iter-defun, создает итератор; вы должны управлять этим
итератором с помощью iter-next, чтобы произошло что-либо
интересное. Каждый вызов генераторной функции создает разный
итератор, каждый со своим собственным состоянием.
Извлекает следующее значение из iterator. Если больше
значений генерировать нет (потому что генераторная функция iterator
вернулась), iter-next сигнализирует условием
iter-end-of-sequence; значение данных, связанное с этим
условием, — это значение, с которым генераторная функция iterator
вернулась.
value передается в итератор и становится значением, к которому
эквивалентно iter-yield. value игнорируется для
первого вызова iter-next для данного итератора, поскольку в начале
генераторной функции iterator генераторная функция не
выполняет никакую форму iter-yield.
Если iterator приостановлен внутри bodyform
unwind-protect и становится недостижимым, Emacs в конечном итоге
выполнит обработчики отмены после прохода сборки мусора. (Обратите внимание, что
iter-yield недопустим внутри unwindforms
unwind-protect.) Чтобы гарантировать, что эти обработчики будут
выполнены до этого, используйте iter-close.
Предоставлен ряд удобных функций, чтобы облегчить работу с итераторами:
Выполняет body, привязывая var к каждому значению, которое производит iterator.
В модуле Common Lisp также есть функции для работы с итераторами. См. Loop Facility в Common Lisp Extensions.
Следующий фрагмент кода демонстрирует некоторые важные принципы работы с итераторами.
(require 'generator)
(iter-defun my-iter (x)
(iter-yield (1+ (iter-yield (1+ x))))
;; Return normally
-1)
(let* ((iter (my-iter 5))
(iter2 (my-iter 0)))
;; Prints 6
(print (iter-next iter))
;; Prints 9
(print (iter-next iter 8))
;; Prints 1; iter and iter2 have distinct states
(print (iter-next iter2 nil))
;; We expect the iter sequence to end now
(condition-case x
(iter-next iter)
(iter-end-of-sequence
;; Prints -1, which my-iter returned normally
(print (cdr x)))))