layout | title | permalink |
---|---|---|
default |
Funciones |
/es/outline/functions.html |
{::options parse_block_html="true" /}
{% comment %}
http://clojurebridge.github.io/curriculum/outline/functions.html
{% endcomment %}
- ¿Qué son las funciones?
- Nombrando funciones
- [bonus] Funciones que reciben otras funciones
map
yreduce
- [bonus] Funciones anónimas
- [bonus] Asignación con
let
Ya 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? {: ng-show="block11" .description}
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. {: ng-show="block11" .description}
Referencia: Basics of Function {: ng-show="block11" .description}
count
,conj
,first
+
,-
,*
,/
- Una porción de código que recibe valores y devuelve un valor
defn
indica que vamos a definir una función.forward-right
es 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. {: ng-show="block21" .description}
(defn forward-right
"Mueve la tortuga especificada a la derecha e inclina su cabeza"
[turtle]
(forward turtle 60)
(right turtle 135))
Para usar
forward-right
, invocamos la función, como hicimos con todas las funciones que ya hemos utilizado. {: ng-show="block31" .description}
(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-len
que además de la tortuga, reciba una distancia hacia adelante. {: ng-show="block41" .description}
(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}}
- Escribir una función
- Ve a
walk.clj
- En el editor, escribe la función
forward-right
que aparece en la presentación (abajo). - (Optional) Guarda
walk.clj
- Selecciona la función
forward-right
entera y pulsa "Eval Selection"
- Usa una función
- Escribe
(forward-right :trinity)
en el panel del REPL. - Repite el paso anterior al menos 8 veces (utiliza la flecha hacia arriba y pulsa Enter).
(defn forward-right
"Mueve a la derecha a la tortuga especificada e inclina su cabeza"
[turtle]
(forward turtle 60)
(right turtle 135))
- Ve a
walk.clj
- En el editor, escribe la función
forward-right-with-len-ang
, que recibe tres argumentos: turtle, len, y angle (extensión deforward-right-with-len
) - Selecciona la función
forward-right-with-len-ang
entera y pulsa Reload Selection. - En el panel del REPL, escribe
(forward-right-with-len-ang :trinity 60 120)
- Repite lo anterior, evaluando varias veces la función en el REPL.
Los nombres de funciones son símbolos, al igual que los símbolos definidos con
def
cuando asignamos nombres a valores. {: ng-show="block61" .description}
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. {: ng-show="block61" .description}
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. {: ng-show="block62" .description}
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?
. {: ng-show="block63" .description}
=
,not=
>
,<
,>=
,<=
true?
,false?
,empty?
,nil?
,vector?
,map?
{: .slide_title .slide}
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. {: ng-show="block71" .description}
Referencia: Higher-order Function {: ng-show="block71" .description}
map
es una función que recibe otra función y una colección.map
invoca 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. {: ng-show="block101" .description}
(map inc [1 2 3]) ;=> (2 3 4)
(map (partial + 90) [0 30 60 90]) ;=> (90 120 150 180)
Referencias: partial
Veamos otra función que recibe una función. Esta función es
reduce
, y se usa para convertir colecciones en un único valor. {: ng-show="block111" .description}
reduce
recibe 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.reduce
hace esto repetidamente hasta que finalmente llega al final de la colección. {: ng-show="block111" .description}
(reduce str (turtle-names)) ;=> ":trinity:neo:oracle:cypher"
(reduce + [30 60 90]) ;=> 180
-
Crea una función llamada
average
que reciba un vector de mapas. -
Usa
[{:angle 30} {:angle 90} {:angle 50}]
como vector de entrada. -
Calcula el valor de :angle.
-
Pista: Necesitarás utilizar las funciones
map
,reduce
ycount
.
Hasta ahora, todas las funciones que vimos tenían nombres, como
+
,str
yreduce
. 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: {: ng-show="block201" .description}
Referencia: Anonymous Function {: ng-show="block201" .description}
(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. {: ng-show="block202" .description}
(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
map
oreduce
, de las cuales ya aprendimos en la sección sobre Funciones. Veamos ejemplos de uso de funciones anónimas: {: ng-show="block203" .description}
(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"
{: .slide_title .slide}
Cuando 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
def
como lo harías fuera de una función. En lugar de eso, deberías usar una forma especial llamadalet
. {: ng-show="block301" .description}
Podemos 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. {: ng-show="block305" .description}
Referencia: Assignment let {: ng-show="block305" .description}
(let [mangoes 3
oranges 5]
(+ mangoes oranges))
;=> 8
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. {: ng-show="block311" .description}
Luego, vemos
let
.let
recibe un vector de nombres y valores intercalados.t1
es le primer nombre, y le asignamos el resultado de(first names)
. También le asignamos el resultado de(last names)
at2
. {: ng-show="block312" .description}
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
,t1
andt2
están definidos. {: ng-show="block313" .description}
Ve a
walk.clj
y escribe la funciónopposite
. Luego, evalúa la funciónopposite
en la última línea de la definición de la función. También, evalúa el ejemplo de uso de la funciónopposite
. {: ng-show="block314" .description}
;; 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))
{% comment %}
🌟 Un enlace más abajo es únicamente para una diapositiva. Ve a README.md 🌟
{% endcomment %}