--- title: Clojure Looprecur localeTitle: Clojure Looprecur --- Возможно, вам придется понять, [`if`](//forum.freecodecamp.com/t/clojure-conditionals/18412) и [`let`](//forum.freecodecamp.com/t/clojure-create-local-variables-with-let/18415) полностью понять рекурсию в Clojure. ## `for` и `while` Clojure не имеет циклов или циклов. Это имеет смысл, если вы думаете об этом. Цикл `for` изменяет переменную, и это не допускается в Clojure. ``` for (var i = 0; i < 10; i++) { console.log(i); } ``` `i++` означает, что мы добавляем его к переменной `i` каждый раз, когда цикл завершается - ясный пример изменяемой переменной. `while` циклы менее явно зависят от изменения переменных, но они, как и для циклов. ``` var i = 0; while (i < 10) { console.log(i); i++; } ``` `while` циклов всегда есть условие, такое как `i < 10` , и будет ломаться, если это условие перестает быть истинным. Это означает, что у них должен быть какой-то побочный эффект (например, добавление 1 к `i` ), чтобы условие в конечном итоге было ложным; в противном случае цикл будет длиться вечно. ## Рекурсия К счастью, у Clojure есть одна петля. Эти циклы используют рекурсию - функцию, которая вызывает себя. Простейшим рекурсивным алгоритмом является поиск положительного числа factorial (5 факториалов, например, `5 * 4 * 3 * 2` ). ``` (defn fact [x] (loop [nx prod 1] ;; this works just like a 'let' binding. (if (= 1 n) ;; this is the base case. prod (recur (dec n) (* prod n))))) ``` ![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ": Ракета:") [IDEOne!](https://ideone.com/3iP3tI) Вы заметите, что `(loop [nx prod 1] ...)` выглядит очень похоже на привязку `let` . Фактически это работает точно так же - здесь мы привязываем `n` к `x` и `prod` 1. Каждая рекурсивная функция имеет «базовый регистр». Это условие, которое делает цикл остановки цикла. В этом случае наш цикл останавливается, если `n = 1` , и возвращает `prod` . Если `n` не равно 1, то цикл повторяется. ``` (recur (dec n) (* prod n)) ``` Эта `recur` функция перезапускает цикл, но с разными привязками. На этот раз `n` не привязано к `x` , а привязано к `(dec n)` (что означает `decrement n` или `n - 1` ), а `prod` привязан к `(* prod n)` . Поэтому, когда мы вызываем функцию, это происходит: ``` (fact 5) ; Loop 1: 5 != 1, so the loop recurs with 4 (5 - 1) and 5 (1 * 5). ; Loop 2: 4 != 1, so the loop recurs with 3 (4 - 1) and 20 (5 * 4). ; Loop 3: 3 != 1, so the loop recurs with 2 (3 - 1) and 60 (20 * 3). ; Loop 4: 2 != 1, so the loop recurs with 1 (2 - 1) and 120 (60 * 2). ; Loop 5: 1 == 1, so the function returns prod, which is now equal to 120. ; => 120 ``` Гениальная вещь о рекурсии состоит в том, что сами переменные никогда не меняются. Единственное, что меняется, это то, о чем _говорят_ `n` и `prod` . Мы никогда не говорим, что `n--` , или `n += 2` . ## Зачем использовать loop / recur? Вам может быть интересно, почему вы будете использовать `loop/recur` а не просто определять функцию, которая вызывает себя. Наша факториальная функция могла быть написана так: ``` (defn fact-no-loop [n] (if (= 1 n) 1 (* n (fact-no-loop (dec n))))) ``` Это более красноречиво и работает аналогичным образом. Почему бы вы _когда_ - _либо_ использовать цикл и повторялись? ### Оптимизация звонков Если вы используете `loop/recur` , то компилятор (программное обеспечение, которое превращает Clojure-код в JTM-байт-код) знает, что вы хотите создать рекурсивный цикл. Это означает, что он пытается изо всех сил оптимизировать ваш код для рекурсии. Давайте сравним скорость `fact` и `fact-no-loop` : ``` (time (fact 20)) ; => "Elapsed time: 0.083927 msecs" ; 2432902008176640000 (time (fact-no-loop 20)) ; => "Elapsed time: 0.064937 msecs" ; 2432902008176640000 ``` ![:rocket:](//forum.freecodecamp.com/images/emoji/emoji_one/rocket.png?v=2 ": Ракета:") [IDEOne!](https://ideone.com/tpC0Xo) В этом масштабе разница незначительна. Фактически, `fact-no-loop` иногда быстрее, чем `fact` из-за непредсказуемого характера компьютерной памяти. Однако в более широком масштабе такая оптимизация может сделать ваш код намного, намного быстрее. ### Реестр вложенности внутри функций `fact-no-loop` работает без `loop/recur` потому что вся функция рекурсивна. Что, если мы хотим, чтобы часть нашей функции использовала рекурсивный цикл, а затем остальную часть, чтобы сделать что-то нерекурсивное? Нам нужно было бы определить две совершенно отдельные функции. Использование `loop/recur` позволяет использовать небольшую анонимную функцию. | [![:point_left:](//forum.freecodecamp.com/images/emoji/emoji_one/point_left.png?v=2 ": Point_left:") Предыдущая](//forum.freecodecamp.com/t/clojure-create-local-variables-with-let/18415) | [![:book:](//forum.freecodecamp.com/images/emoji/emoji_one/book.png?v=2 ":книга:") Главная ![:book:](//forum.freecodecamp.com/images/emoji/emoji_one/book.png?v=2 ":книга:")](//forum.freecodecamp.com/t/clojure-resources/18422) | следующий ![:point_right:](//forum.freecodecamp.com/images/emoji/emoji_one/point_right.png?v=2 ": Point_right:") | | [Пусть привязки](//forum.freecodecamp.com/t/clojure-create-local-variables-with-let/18415) | [Содержание](//forum.freecodecamp.com/t/clojure-resources/18422) | Чтобы добавить |