97 lines
5.3 KiB
Markdown
97 lines
5.3 KiB
Markdown
---
|
||
title: Clojure Looprecur
|
||
localeTitle: Clojure Looprecur
|
||
---
|
||
Você pode precisar entender [`if`](//forum.freecodecamp.com/t/clojure-conditionals/18412) e [`let`](//forum.freecodecamp.com/t/clojure-create-local-variables-with-let/18415) entender completamente a recursão no Clojure.
|
||
|
||
## `for` e `while`
|
||
|
||
O Clojure não possui loops ou loops while. Isso faz sentido, se você pensar sobre isso. Um loop `for` altera uma variável e isso não é permitido no Clojure.
|
||
```
|
||
for (var i = 0; i < 10; i++) {
|
||
console.log(i);
|
||
}
|
||
```
|
||
|
||
`i++` significa que adicionamos um à variável `i` toda vez que o loop termina - um exemplo claro de uma variável sendo mutada.
|
||
|
||
`while` loops são menos dependentes de variáveis variáveis, mas são, da mesma forma que os loops.
|
||
```
|
||
var i = 0;
|
||
while (i < 10) {
|
||
console.log(i);
|
||
i++;
|
||
}
|
||
```
|
||
|
||
`while` loops sempre tem uma condição, como `i < 10` , e vai quebrar se essa condição não for mais verdadeira. Isso significa que eles têm que ter algum tipo de efeito colateral (como adicionar 1 a `i` ) para que a condição seja eventualmente falsa; caso contrário, o loop duraria para sempre.
|
||
|
||
## Recursão
|
||
|
||
Felizmente, o Clojure tem um loop de algum tipo. Esses loops usam recursão - uma função que chama a si mesma. O algoritmo recursivo mais simples é o de encontrar um fatorial numérico positivo (5 fatorial, por exemplo, igual a `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 ":foguete:") [IDEOne isso!](https://ideone.com/3iP3tI)
|
||
|
||
Você notará que `(loop [nx prod 1] ...)` é bastante semelhante a um `let` binding. Na verdade, funciona da mesma maneira - aqui, ligamos `n` para `x` e `prod` para 1.
|
||
|
||
Cada função recursiva tem um "caso base". Esta é a condição que faz com que o loop pare de fazer loop. Nesse caso, nosso loop pára se `n = 1` e retorna `prod` . Se `n` não for igual a 1, o loop se repetirá.
|
||
```
|
||
(recur (dec n) (* prod n))
|
||
```
|
||
|
||
Essa função `recur` reinicia o loop, mas com ligações diferentes. Desta vez, `n` não está ligado a `x` , mas está ligado a `(dec n)` (o que significa `decrement n` ou `n - 1` ), e `prod` está ligado a `(* prod n)` .
|
||
|
||
Então, quando chamamos a função, isso é o que acontece:
|
||
```
|
||
(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
|
||
```
|
||
|
||
A coisa engenhosa sobre a recursão é que as variáveis em si nunca são alteradas. A única coisa que muda é o que `n` e `prod` _referem_ . Nós nunca dizemos, `n--` ou `n += 2` .
|
||
|
||
## Por que usar loop / recorrer?
|
||
|
||
Você pode estar se perguntando por que você usaria `loop/recur` vez de simplesmente definir uma função que chama a si mesma. Nossa função fatorial poderia ter sido escrita assim:
|
||
```
|
||
(defn fact-no-loop [n]
|
||
(if (= 1 n)
|
||
1
|
||
(* n (fact-no-loop (dec n)))))
|
||
```
|
||
|
||
Isso é mais conciso e funciona de maneira semelhante. Por que você _nunca_ usar loop e se repetem?
|
||
|
||
### Otimização de Chamadas
|
||
|
||
Se você usar `loop/recur` , o compilador (o software que transforma o código Clojure em bytecode da JVM) sabe que você deseja criar um loop recursivo. Isso significa que ele tenta o máximo para otimizar seu código para recursão. Vamos comparar a velocidade do `fact` e o `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 ":foguete:") [IDEOne isso!](https://ideone.com/tpC0Xo)
|
||
|
||
Nesta escala, a diferença é insignificante. Na verdade, o `fact-no-loop` é ocasionalmente mais rápido que o `fact` devido à natureza imprevisível da memória do computador. No entanto, em uma escala maior, esse tipo de otimização pode tornar seu código muito mais rápido.
|
||
|
||
### Recursão de aninhamento dentro de funções
|
||
|
||
`fact-no-loop` funciona sem `loop/recur` porque a função inteira é recursiva. E se quiséssemos que parte de nossa função usasse um loop recursivo e depois o resto fizesse algo não recursivo? Teríamos que definir duas funções totalmente separadas. Usando o `loop/recur` nos permite usar uma função pouco anônima.
|
||
|
||
| [![:point_left:](//forum.freecodecamp.com/images/emoji/emoji_one/point_left.png?v=2 ": point_left:") Anterior](//forum.freecodecamp.com/t/clojure-create-local-variables-with-let/18415) | [![:book:](//forum.freecodecamp.com/images/emoji/emoji_one/book.png?v=2 ":livro:") Casa ![:book:](//forum.freecodecamp.com/images/emoji/emoji_one/book.png?v=2 ":livro:")](//forum.freecodecamp.com/t/clojure-resources/18422) | Próximo ![:point_right:](//forum.freecodecamp.com/images/emoji/emoji_one/point_right.png?v=2 ": point_right:") |
|
||
| [Vamos Bindings](//forum.freecodecamp.com/t/clojure-create-local-variables-with-let/18415) | [Índice](//forum.freecodecamp.com/t/clojure-resources/18422) | Ser adicionado | |