97 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			97 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| ---
 | ||
| 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))))) 
 | ||
| ```
 | ||
| 
 | ||
|  [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 
 | ||
| ```
 | ||
| 
 | ||
|  [IDEOne!](https://ideone.com/tpC0Xo)
 | ||
| 
 | ||
| В этом масштабе разница незначительна. Фактически, `fact-no-loop` иногда быстрее, чем `fact` из-за непредсказуемого характера компьютерной памяти. Однако в более широком масштабе такая оптимизация может сделать ваш код намного, намного быстрее.
 | ||
| 
 | ||
| ### Реестр вложенности внутри функций
 | ||
| 
 | ||
| `fact-no-loop` работает без `loop/recur` потому что вся функция рекурсивна. Что, если мы хотим, чтобы часть нашей функции использовала рекурсивный цикл, а затем остальную часть, чтобы сделать что-то нерекурсивное? Нам нужно было бы определить две совершенно отдельные функции. Использование `loop/recur` позволяет использовать небольшую анонимную функцию.
 | ||
| 
 | ||
| | [ Предыдущая](//forum.freecodecamp.com/t/clojure-create-local-variables-with-let/18415) | [ Главная ](//forum.freecodecamp.com/t/clojure-resources/18422) | следующий  |  
 | ||
| | [Пусть привязки](//forum.freecodecamp.com/t/clojure-create-local-variables-with-let/18415) | [Содержание](//forum.freecodecamp.com/t/clojure-resources/18422) | Чтобы добавить | |