Seguimos con Clojure...
- Los métodos Java no son funciones de Clojure
- No se pueden almacenar ni pasar como argumentos
- Puede envolverlos en funciones cuando sea necesario
# Python 2
python -m SimpleHTTPServer
# Python 3
python -m http.server
Podemos poner el puerto si lo deseamos por defecto es 8080, por ejemplo:
python -m SimpleHTTPServer 9999
Y listo si accedes a http://localhost:8080 (en caso que no hayas cambiado el puerto) podes ver los archivos.
;; bindings name is defined here
;; ------------ ----------------------
(let [name value] (code that uses name))
Cada let puede definir 0 o más enlaces y puede tener 0 o más expresiones en el cuerpo.
(let [x 1
y 2]
(+ x y))
Esta expresión let crea dos enlaces locales para x e y. La expresión (+ x y) está en el ámbito léxico de let y resuelve x en 1 ey en 2. Fuera de la expresión let, x e y no tendrán un significado continuo, a menos que ya estén vinculados a un valor.
(defn messenger [msg]
(let [a 7
b 5
c (clojure.string/capitalize msg)]
(println a b c)
) ;; end of let scope
) ;; end of function
La función messenger toma un argumento msg. Aquí, defn también crea un alcance léxico para msg; solo tiene significado dentro de la función.
Dentro de ese alcance de función, let crea un nuevo alcance para definir a, b y c. Si intentáramos usar una expresión después de let, el compilador reportaría un error.
Clojure tiene clusuras, la expresión fn crea una "clausura". La clausura existe en el ámbito léxico circundante (como msg, a, boc arriba) y captura sus valores más allá del ámbito léxico.
(defn messenger-builder [greeting]
(fn [who] (println greeting who))) ; closes over greeting
;; greeting provided here, then goes out of scope
(def hello-er (messenger-builder "Hello"))
;; greeting value still available because hello-er is a closure
(hello-er "world!")
;; Hello world!
(apply f '(1 2 3 4)) ;; same as (f 1 2 3 4)
(apply f 1 '(2 3 4)) ;; same as (f 1 2 3 4)
(apply f 1 2 '(3 4)) ;; same as (f 1 2 3 4)
(apply f 1 2 3 '(4)) ;; same as (f 1 2 3 4)
Las 4 de estas llamadas son equivalentes a (f 1 2 3 4). apply es útil cuando los argumentos se le entregan como una secuencia, pero debe invocar la función con los valores en la secuencia.
Por ejemplo, puede usar aplicar para evitar escribir esto:
(defn plot [shape coords] ;; coords is [x y]
(plotxy shape (first coords) (second coords)))
En su lugar, puede simplemente escribir:
(defn plot [shape coords]
(apply plotxy shape coords))
Hay una forma más corta para la sintaxis de la función anónima fn implementada en el lector de Clojure: # (). Esta sintaxis omite la lista de parámetros y nombra los parámetros en función de su posición.
% se usa para un solo parámetro
% 1,% 2,% 3, etc. se utilizan para varios parámetros
% & se usa para los parámetros restantes (variadic)
Las funciones anónimas anidadas crearían una ambigüedad ya que los parámetros no tienen nombre, por lo que no se permite la anidación.
;; Equivalent to: (fn [x] (+ 6 x))
#(+ 6 %)
;; Equivalent to: (fn [x y] (+ x y))
#(+ %1 %2)
;; Equivalent to: (fn [x y & zs] (println x y zs))
#(println %1 %2 %&)
(defn greet [name] (str "Hello, " name))
(def greet (fn [name] (str "Hello, " name)))
Clojure como buen lenguaje funcional tiene funciones anónimas, como por ejemplo :
;; params body
;; --------- -----------------
(fn [message] (println message) )
Dado que la función anónima no tiene nombre, no se puede hacer referencia a ella más adelante. Más bien, la función anónima generalmente se crea en el punto en que se pasa a otra función. O es posible invocarlo inmediatamente (no es lo común):
;; operation (function) argument
;; -------------------------------- --------------
( (fn [message] (println message)) "Hello world!" )
Aquí definimos la función anónima en la posición de función de una expresión más grande que inmediatamente invoca la expresión con el argumento.
Muchos lenguajes tienen tanto declaraciones, que imperativamente hacen algo y no devuelven un valor, como expresiones que lo hacen. Clojure solo tiene expresiones que devuelven un valor. Más adelante veremos que esto incluye incluso expresiones de control de flujo como el if.
Hablando mal y pronto es como en java que ponemos los 3 puntos en los argumentos de la función, algo así :
void printArgs(String... strings) {
for (String string : strings) {
System.out.println(string);
}
}
Entonces podemos pasarle un número n de argumentos :
printArgs("hello"); // short for printArgs( ["hello"] )
printArgs("hello", "world"); // short for printArgs( ["hello", "world"] )
En clojure, las funciones también pueden definir un número variable de parámetros. Los parámetros de las variables deben aparecer al final de la lista de parámetros. Se recopilarán en una secuencia para que los utilice la función.
El comienzo de los parámetros variables está marcado con &. Por ejemplo :
(defn hello [greeting & who]
(println greeting who))
Esta función toma un saludo de parámetro y un número variable de parámetros (0 o más) que se recopilarán en una lista denominada who. Podemos ver esto invocándolo con 3 argumentos:
user=> (hello "Hello" "world" "class")
Hello (world class)
Se puede ver que cuando println imprime who, se imprime como una lista de dos elementos que se recopilaron.
Las funciones se pueden definir para tomar diferentes números de parámetros. Funciona como el pattetn matching, veamos un ejemplo :
(defn messenger
([] (messenger "Hello world!"))
([msg] (println msg)))
Esta función esta declarada 2 veces con 0 parámetros y 1 parámetro. La función de 0 parámetros llama a la función de 1 parámetro con un valor predeterminado para imprimir. Invocamos estas funciones pasando el número apropiado de argumentos:
user=> (messenger)
Hello world!
nil
user=> (messenger "Hello class!")
Hello class!
nil
defn define una función nombrada:
;; name params body
;; ----- ------ -------------------
(defn greet [name] (str "Hello, " name) )
Esta función tiene un solo parámetro, sin embargo, se puede incluir cualquier número de parámetros en el vector params.
Luego podemos llamar la función de esta manera :
user=> (greet "mundo")
"Hello, mundo"
|
Una de las cosas más comunes que se hace al aprender un lenguaje es imprimir valores. Clojure proporciona varias funciones para imprimir valores.
println y print traducirán los caracteres impresos especiales (como nuevas líneas y tabulaciones) a su forma impresa y omitirán las comillas en las cadenas. A menudo usamos println para depurar funciones o imprimir un valor en el REPL. println toma cualquier número de argumentos e interpone un espacio entre el valor impreso de cada argumento:
user=> (println "¿Qué es esto?" (+ 1 2))
¿Qué es esto? 3
nil
Tengamos en cuenta que (println "¿Qué es esto?" (+ 1 2)) no imprimió las comillas circundantes y no es una cadena que el lector pueda volver a leer como datos.
Para ese propósito, podemos usar prn o pr para imprimir como datos:
user=> (prn "one\n\ttwo")
"one\n\ttwo"
nil
Dependiendo del contexto, es posible que prefieramos usar println o print o pr o prn.
Con def podemos guardar un valor en una variable pero ojo al piojo que en los lenguajes funcionales las variables no varían. Son constantes donde guardamos un valor, para utilizarlo más adelante o puede ser un pero eso es otro tema... Lo importante, es que en Clojure estamos lejos del concepto de mutabilidad.
Para definir una variable podemos hacer:
user=> (def x 6)
#'user/x