Clojure запрещает мутацию локальных переменных ради безопасности потоков, но все же можно писать циклы даже без мутации.В каждом прогоне цикла вы хотите my-list
иметь разное значение, но это может быть достигнуто с помощью рекурсии, а также:
(let [step (fn [i my-list]
(if (< i 5)
my-list
(recur (inc i) (cons i my-list))))]
(step 0 nil))
Clojure также есть способ «просто сделать зацикливание», не делая новую функцию , а именно loop
. Он выглядит как let
, но вы также можете перейти к началу своего тела, обновить привязки и снова запустить тело с помощью recur
.
(loop [i 0
my-list nil]
(if (< i 5)
my-list
(recur (inc i) (cons i my-list))))
«Обновление» параметры с рекурсивной хвост вызов может выглядеть очень похоже на мутирует переменную, но есть одно важное отличие: при вводе my-list
в коде Clojure, его значение всегда будет всегда значение из my-list
. Если вложенная функция закрывается над my-list
, и цикл продолжается до следующей итерации, вложенная функция всегда будет видеть значение, которое было my-list
, когда была создана вложенная функция. Локальная переменная всегда может быть заменена ее значением, а переменная, которую вы имеете после рекурсивного вызова, в некотором смысле является другой переменной.
(компилятор Clojure выполняет оптимизацию, так что никакого дополнительного пространства не требуется для этой «новой переменной»: Когда переменная нужно помнить, его значение копируется и когда recur
называется старая переменная повторно.)
В Common Lisp это будет короче, так как (цикл для i downfrom 4 to 0 collect i). –