11.5 Итерация

Итерация означает многократное выполнение части программы. Например, вы можете захотеть повторить вычисление один раз для каждого элемента списка или один раз для каждого целого числа от 0 до n. Вы можете сделать это в Emacs Lisp с помощью специальной формы while:

Специальная форма: while условие формы...

while сначала вычисляет условие. Если результат не равен nil, он вычисляет формы в текстовом порядке. Затем он повторно вычисляет условие, и если результат не равен nil, он снова вычисляет формы. Этот процесс повторяется до тех пор, пока условие не вычисляется в nil.

Нет ограничений на количество итераций, которое может произойти. Цикл будет продолжаться до тех пор, пока условие не вычисляется в nil, или пока ошибка или throw не прервет его (см. Нелокальные выходы).

Значение формы while всегда равно nil.

(setq num 0)
     ⇒ 0
(while (< num 4)
  (princ (format "Iteration %d." num))
  (setq num (1+ num)))
     -| Iteration 0.
     -| Iteration 1.
     -| Iteration 2.
     -| Iteration 3.
     ⇒ nil

Чтобы написать цикл повторение до, который выполнит что-то на каждой итерации, а затем проверит условие завершения, поместите тело, за которым следует условие завершения, в progn в качестве первого аргумента while, как показано здесь:

(while (progn
         (forward-line 1)
         (not (looking-at "^$"))))

Это переходит вперед на одну строку и продолжает двигаться по строкам до тех пор, пока не достигнет пустой строки. Примечательно, что у while нет тела, только условие завершения (которое также выполняет реальную работу по перемещению курсора).

Макросы dolist и dotimes предоставляют удобные способы написания двух распространенных типов циклов.

Макрос: dolist (var list [result]) body...

Эта конструкция выполняет body один раз для каждого элемента list, локально связывая переменную var для хранения текущего элемента. Затем она возвращает значение вычисления result или nil, если result опущен. Например, вот как вы можете использовать dolist для определения функции reverse:

(defun reverse (list)
  (let (value)
    (dolist (elt list value)
      (setq value (cons elt value)))))
Макрос: dotimes (var count [result]) body...

Эта конструкция выполняет body один раз для каждого целого числа от 0 (включительно) до count (исключительно), связывая переменную var с целым числом для текущей итерации. Затем она возвращает значение вычисления result или nil, если result опущен. Использование result устарело. Вот пример использования dotimes для выполнения чего-либо 100 раз:

(dotimes (i 100)
  (insert "I will not obey absurd orders\n"))