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

5.3 KiB
Raw Blame History

title localeTitle
Clojure Looprecur Clojure Looprecur

Você pode precisar entender if e let 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: IDEOne isso!

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: IDEOne isso!

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: Anterior | :book: Casa :book: | Próximo :point_right: |
| Vamos Bindings | Índice | Ser adicionado |