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

5.4 KiB

title localeTitle
Clojure Looprecur Clojure Looprecur

Es posible que necesite para entender if y let de comprender plenamente la recursividad en Clojure.

for y while

Clojure no tiene para bucles o mientras bucles. Esto tiene sentido, si lo piensas. Un bucle for cambia una variable, y eso no está permitido en Clojure.

for (var i = 0; i < 10; i++) { 
  console.log(i); 
 } 

i++ significa que agregamos uno a la variable i cada vez que finaliza el bucle, un claro ejemplo de una variable que se está mutando.

while bucles dependen menos obviamente de las variables cambiantes, pero lo son, tanto como lo son para los bucles.

var i = 0; 
 while (i < 10) { 
  console.log(i); 
  i++; 
 } 

while bucles siempre tienen una condición, como i < 10 , y se romperán si esa condición ya no es verdadera. Esto significa que deben tener algún tipo de efecto secundario (como agregar 1 a i ) para que la condición sea finalmente falsa; De lo contrario, el bucle duraría para siempre.

Recursion

Afortunadamente, Clojure tiene una serie de bucles. Estos bucles usan recursión, una función que se llama a sí misma. El algoritmo recursivo más simple es encontrar un factorial de número positivo (5 factorial, por ejemplo, es 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: ¡IDEAe!

Notarás que (loop [nx prod 1] ...) parece bastante a un enlace let . En realidad, funciona de la misma manera: aquí, enlazamos n a x , y prod . A 1.

Cada función recursiva tiene un "caso base". Esta es la condición que hace que el bucle se detenga en bucle. En este caso, nuestro bucle se detiene si n = 1 , y devuelve prod . Si n no es igual a 1, entonces el bucle se repite.

(recur (dec n) (* prod n)) 

Esta función de recur reinicia el bucle, pero con diferentes enlaces. Esta vez, n no está vinculado a x , sino que está vinculado a (dec n) (lo que significa decrement n , o n - 1 ), y prod está vinculado a (* prod n) .

Entonces cuando llamamos a la función, esto es lo que sucede:

(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 

Lo ingenioso de la recursión es que las variables en sí nunca se cambian. Lo único que cambia es a lo que se refieren n y prod . Nunca decimos, n-- , o n += 2 .

¿Por qué usar loop / recurrir?

Quizás se esté preguntando por qué usaría loop/recur lugar de simplemente definir una función que se llame a sí misma. Nuestra función factorial podría haberse escrito así:

(defn fact-no-loop [n] 
  (if (= 1 n) 
    1 
    (* n (fact-no-loop (dec n))))) 

Esto es más conciso, y funciona de una manera similar. ¿Por qué alguna vez usarías loop y recurrir?

Optimización de llamadas de cola

Si usa loop/recur , entonces el compilador (el software que convierte el código Clojure en un código de bytes JVM) sabe que desea crear un bucle recursivo. Esto significa que hace todo lo posible para optimizar su código para la recursión. Comparemos la velocidad de fact y fact-no-loop :

(time (fact 20)) 
 ; => "Elapsed time: 0.083927 msecs" 
 ;    2432902008176640000 
 (time (fact-no-loop 20)) 
 ; => "Elapsed time: 0.064937 msecs" 
 ;    2432902008176640000 

:rocket: ¡IDEAe!

A esta escala, la diferencia es despreciable. De hecho, el fact-no-loop es ocasionalmente más rápido que el fact debido a la naturaleza impredecible de la memoria de la computadora. Sin embargo, en una escala más grande, este tipo de optimización puede hacer que su código sea mucho más rápido.

Anidamiento recursivo dentro de funciones

fact-no-loop funciona sin loop/recur porque la función completa es recursiva. ¿Qué pasaría si quisiéramos que parte de nuestra función usara un bucle recursivo y luego el resto para hacer algo no recursivo? Tendríamos que definir dos funciones completamente separadas. Usar loop/recur nos permite usar una pequeña función anónima en su lugar.

| :point_left: Anterior | :book: Casa :book: | Siguiente :point_right: |
| Dejar enlaces | Tabla de Contenidos | Para ser añadido |