freeCodeCamp/guide/russian/clojure/looprecur/index.md

97 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
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) | Чтобы добавить |