map y reduceletYa has visto algunas funciones, como
count,conj,first, yrest. También usamos funciones en toda la arimética que hicimos hasta ahora:+,-,*, y/. Pero, ¿qué significa ser una función?
Una función es una porción de código discreta e independiente, que recibe algunos valores (llamados parámetros o argumentos), y devuelve un valor.
Referencia: Basics of Function
count, conj, first+, -, *, /
defnindica que vamos a definir una función.forward-rightes el nombre de esta función.- La cadena de texto en la siguiente línea es la documentación de la función, que explica qué hace la función. Esta línea es opcional.
[turtle]es la lista de argumentos. Aquí tenemos un argumento llamadoturtle.(forward turtle 60) (right turtle 135)es el cuerpo de la función. Esto es lo que se ejecuta cuando usamos la función.
(defn forward-right
"Mueve la tortuga especificada a la derecha e inclina su cabeza"
[turtle]
(forward turtle 60)
(right turtle 135))
forward-right Para usar
forward-right, invocamos la función, como hicimos con todas las funciones que ya hemos utilizado.
(forward-right :trinity) ;=> {:trinity {:angle 135}}
(forward-right :neo) ;=> {:neo {:angle 135}}
Las funciones también pueden tomar más de un argumento. Hagamos una función
forward-right-with-lenque además de la tortuga, reciba una distancia hacia adelante.
(defn forward-right-with-len
"Dados turtle y length, adelanta turtle e inclina su cabeza"
[turtle len]
(forward turtle len)
(right turtle 135))
(forward-right-with-len :trinity 90) ;=> {:trinity {:angle 135}}
(forward-right-with-len :neo 80) ;=> {:neo {:angle 135}}
walk.cljforward-right que aparece en la presentación (abajo).walk.cljforward-right entera y pulsa “Eval Selection”(forward-right :trinity) en el panel del REPL.(defn forward-right
"Mueve a la derecha a la tortuga especificada e inclina su cabeza"
[turtle]
(forward turtle 60)
(right turtle 135))
walk.cljforward-right-with-len-ang, que recibe tres
argumentos: turtle, len, y angle (extensión de forward-right-with-len)forward-right-with-len-ang entera y pulsa Reload Selection.(forward-right-with-len-ang :trinity 60 120)Los nombres de funciones son símbolos, al igual que los símbolos definidos con
defcuando asignamos nombres a valores.
Los símbolos deben comenzar con un carácter no numérico, y pueden contener carácteres alfanuméricos, *, +, !, -, _, y ?. Esta flexibilidad es importante con las funciones, ya que hay ciertas formas de expresiones que usaremos.
Clojure tiene dos clases de funciones:
- funciones que devuelven un valor
- funciones que devuelven true o false A las funciones de la segunda clase se las llama predicados.
En Clojure,
=es una función predicado, lo cual puede ser un hecho sorprendente. Además, como muchos otros lenguajes, Clojure tiene funciones predicado para probar mayor o igual, menor o igual, etc. Mayormente las funciones predicado terminan con?.
=,not=>,<,>=,<=true?,false?,empty?,nil?,vector?,map?
Algunas de las funciones más poderosas que puedes utilizar con colecciones pueden tomar otras funciones como argumentos. Ésta es una de las cosas más mágicas acerca de Clojure–y varios otros lenguajes de programación. Ésta es una idea complicada, así que puede que no parezca tener mucho sentido al principio. Veamos un ejemplo para aprender más sobre esto.
Referencia: Higher-order Function
map
mapes una función que recibe otra función y una colección.mapinvoca la función recibida en cada elemento de la colección, y luego devuelve una nueva colección con los resultados de esas invocaciones. Éste es un concepto extraño, pero es uno de los conceptos más importantes de Clojure y de la programación funcional en general.
(map inc [1 2 3]) ;=> (2 3 4)
(map (partial + 90) [0 30 60 90]) ;=> (90 120 150 180)
Referencias: partial
reduceVeamos otra función que recibe una función. Esta función es
reduce, y se usa para convertir colecciones en un único valor.
reducerecibe los dos primeros elementos de la colección que recibió, e invoca la función provista sobre esos elementos. Luego, vuelve a invocar la misma función – sólo que esta vez lo hace usando el resultado de la invocación anterior, junto con el siguiente elemento de la colección.reducehace esto repetidamente hasta que finalmente llega al final de la colección.
(reduce str (turtle-names)) ;=> ":trinity:neo:oracle:cypher"
(reduce + [30 60 90]) ;=> 180
average que reciba un vector de mapas.[{:angle 30} {:angle 90} {:angle 50}] como vector de entrada.Calcula el valor de :angle.
map, reduce y count.Hasta ahora, todas las funciones que vimos tenían nombres, como
+,stryreduce. Pero las funciones no necesitan nombres, de la misma manera que los valores no necesitan nombres. A las funciones que no tienen nombre las llamamos funciones anónimas. Una función anónima se crea confn, de esta forma:
Referencia: Anonymous Function
(fn [s1 s2] (str s1 " " s2))
Antes de continuar, debes entender que siempre eres libre de nombrar tus funciones. Hacer eso no tiene nada de malo. Sin embargo, hay muchísimo código Clojure con funciones anónimas, por lo que debes ser capaz de entenderlo.
(defn join-with-space
[s1 s2]
(str s1 " " s2))
Por qué querrías usar funciones anónimas? Las funciones anónimas pueden ser muy útiles cuando tenemos funciones que reciben otras funciones, como
maporeduce, de las cuales ya aprendimos en la sección sobre Funciones. Veamos ejemplos de uso de funciones anónimas:
(map (fn [t] (forward t 45)) (turtle-names))
;=> ({:trinity {:length 45}} {:neo {:length 45}} {:oracle {:length
45}} {:cypher {:length 45}})
(reduce (fn [x y] (+ x y)) [1 2 3]) ;=> 6
(reduce (fn [a b] (str a ", " b)) (map name (turtle-names)))
;=> "trinity, neo, oracle, cypher"
letCuando estás creando funciones, puedes querer asignar nombres a valores para poder reutilizar esos valores o hacer tu código más legible. Sin embargo, dentro de una función, no deberías usar
defcomo lo harías fuera de una función. En lugar de eso, deberías usar una forma especial llamadalet.
letPodemos asignar un nombre a un valor utilizando
let, igual que condef. Cuando un nombre es asignado a un valor, el nombre se llama símbolo.
Referencia: Assignment let
(let [mangoes 3
oranges 5]
(+ mangoes oranges))
;=> 8
let
Esta es la función más complicada que hemos visto hasta ahora, así que vamos paso por paso. Primero, tenemos el nombre de la función, el string de documentación, y los argumentos, igual que en otras funciones.
Luego, vemos
let.letrecibe un vector de nombres y valores intercalados.t1es le primer nombre, y le asignamos el resultado de(first names). También le asignamos el resultado de(last names)at2.
Luego del vector de nombres y valores, está el cuerpo del
let. Igual que el cuerpo de una función, éste se ejecuta y devuelve un valor Dentro dellet,t1andt2están definidos.
Ve a
walk.cljy escribe la funciónopposite. Luego, evalúa la funciónoppositeen la última línea de la definición de la función. También, evalúa el ejemplo de uso de la funciónopposite.
;; function definition
(defn opposite
"Dada una coleción de nombres de tortugas, mueve dos de ella en direcciones diferentes."
[names]
(let [t1 (first names)
t2 (last names)]
(forward t1 40)
(backward t2 30)))
;; function usage
(opposite (turtle-names))
Vuelve a la primera diapositiva, o ve al outline del currículum.