Seguimos con Clojure...
(defn greet [name] (str "Hello, " name))
(def greet (fn [name] (str "Hello, " name)))
(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
Antes de empezar a hablar del Relp de Clojure, tenemos que saber un aspecto importante del Relp es que Clojure siempre compila la expresión antes de ejecutarla; Clojure siempre se compila en bytecode de JVM. No hay intérprete de Clojure.
El Relp de Clojure esta recopado, trae como unos trucos, que nos hacen la vida más fácil. Por ejemplo, algunos símbolos especiales recuerdan los resultados de evaluar las últimas tres expresiones:
user=> (doc doc)
-------------------------
clojure.repl/doc
([name])
Macro
Prints documentation for a var or special form given its name,
or for a spec if given a keyword
nil
A veces es útil suspender la evaluación, en particular para símbolos y listas. A veces, un símbolo debería ser simplemente un símbolo sin buscar a qué se refiere:
user=> 'x
x
user=> '(1 2 3)
(1 2 3)
Un error confuso que puede suceder es el resultado de intentar evaluar accidentalmente una lista de datos como si fuera un código:
user=> (1 2 3)
Execution error (ClassCastException) at user/eval156 (REPL:1).
class java.lang.Long cannot be cast to class clojure.lang.IF
Esto porque nos olvidamos el '
Dejo link: https://clojure.org/guides/learn/syntax
Si sabes lisp, clojure es casi natural :
Este diagrama se puede ver la diferencia entre la sintaxis en verde (la estructura de datos de Clojure producida por el Reader) y la semántica en azul (cómo el tiempo de ejecución de Clojure entiende esos datos).
La mayoría de las formas literales de Clojure se evalúan a sí mismas, excepto los símbolos y las listas. Los símbolos se utilizan para referirse a otra cosa y cuando se evalúan, devuelven a qué se refieren. Las listas (como en el diagrama) se evalúan como invocaciones.
En el diagrama, (+ 3 4) se lee como una lista que contiene el símbolo (+) y dos números (3 y 4). El primer elemento (donde se encuentra +) se puede llamar "posición de función", es decir, un lugar para encontrar la cosa a invocar. Si bien las funciones son algo obvio para invocar, también hay algunos operadores especiales conocidos para el tiempo de ejecución, macros y un puñado de otras cosas invocables.
Considerando la evaluación de la expresión anterior:
Muchos lenguajes tienen tanto declaraciones como expresiones, donde las declaraciones tienen algún efecto con estado pero no devuelven un valor. En Clojure, todo es una expresión que se evalúa como un valor. Algunas expresiones (pero no la mayoría) también tienen efectos secundarios.
Dejo link: https://clojure.org/guides/learn/syntax
En python tienen esta forma
lambda argument: manipulate(argument)
Veamos un ejemplo:
add = lambda x, y: x + y
print(add(3, 5))
# Output: 8
Veamos lambda en acción :
a = [(1, 2), (4, 1), (9, 10), (13, -3)]
a.sort(key=lambda x: x[1])
print(a)
# Output: [(13, -3), (4, 1), (1, 2), (9, 10)]
No solo lo podemos utilizar para ordenar, pero les dejo otra versión, esta vez en paralelo :
data = zip(list1, list2)
data = sorted(data)
list1, list2 = map(lambda t: list(t), zip(*data))
Como funcióna o lee las expresiones Clojure?
En Java, el código fuente (archivos .java) se lee como caracteres por el compilador (javac), que produce código de bytes (archivos .class) que la JVM puede cargar. Es decir un proceso tipico de compilación, se lee y compila el archivo fuente y produce bytecode que corre sobre la JVM. Sería algo así :
En Clojure, se lee el código fuente como caracteres y esto se puede de leer desde un archivo .clj o recibir una serie de expresiones de forma interactiva. El lector produce datos de Clojure. El compilador de Clojure luego produce el código de bytes para la JVM. Sería algo así :
Hay dos puntos importantes:
Como la mayoría de los lenguajes Clojure tiene literales que son los valores que normalmente usamos.
Ojo los ; son comentarios al final de la línea.
Tipos numéricos
42 ; entero
-1,5 ; punto flotante
22/7 ; fracción
Los enteros se leen como enteros de 64 bits de precisión fija cuando están dentro del rango y precisión arbitraria en caso contrario. Se puede usar una N al final para forzar una precisión arbitraria. Clojure también admite la sintaxis de Java para enteros octal (prefijo 0), hexadecimal (prefijo 0x) y radix arbitrario (prefijo con base y luego r).
Los valores de coma flotante se leen como flotantes de 64 bits de doble precisión o precisión arbitraria con un sufijo M. También se admite la notación exponencial. Los valores simbólicos especiales ## Inf, ## - Inf y ## NaN representan valores de infinito positivo, infinito negativo y "no es un número", respectivamente.
Tambien tenemos caracteres :
"Hola" ; String
\e ; caracter
# "[0-9] +" ; expresión regular
Las cadenas están contenidas entre comillas dobles y pueden abarcar varias líneas. Los caracteres individuales se representan con una barra invertida al principio. Hay algunos caracteres especiales con nombre: \ newline \ spec \ tab, etc. Los caracteres Unicode se pueden representar con \ uNNNN o en octal con \ oNNN.
Las expresiones regulares literales son cadenas con un # al principio. Estos se compilan en objetos java.util.regex.Pattern.
Tambien tenemos símbolos e identifidores :
map ; símbolo
+ ; símbolo
clojure.core / + ; símbolo con espacio de nombres
nil ; valor nulo
true false ; booleans
: alpha ; palabra clave
:release/alpha ; palabra clave con espacio de nombres
Los símbolos se componen de letras, números y otros signos de puntuación y se utilizan para referirse a otra cosa, como una función, valor, espacio de nombres, etc. Los símbolos pueden tener opcionalmente un espacio de nombres, separados por una barra inclinada del nombre.
Hay tres símbolos especiales que se leen como tipos diferentes: nil es el valor nulo y verdadero y falso son los valores booleanos.
Las palabras clave comienzan con dos puntos iniciales y siempre se evalúan por sí mismas. Se utilizan con frecuencia como valores enumerados o nombres de atributos en Clojure.
Otra cosa son las colecciones Clojure también incluye sintaxis literal para cuatro tipos de colección:
'(1 2 3); lista
[1 2 3]; vector
#{1 2 3}; conjunto
{:a 1,:b 2}; mapa
Hablaremos de colecciones más adelante, por ahora estuvo bien este post.
Cloujure es un lenguaje funcional de tipado dinamico que corre sobre la maquina virtual java y es muy similar a lisp.
Antes de empezar vamos a instalarlo. Yo tengo instalado sdkman, por lo tanto voy a utilizarlo instalando Leiningen :
sdk install leiningen
Hace mucho que no escribo sobre Clojure, por lo tanto estuve googleando a ver a donde anda...
Y me encontré que su ecosistema ha crecido bastante, no muy rápido pero a paso seguro.Si no lo sabían, podemos crear nuestras aplicaciones Clojure en Leiningen que sería el maven de Clojure.
A la vez podemos utilizar Datomic para guardar datos en la base de datos nosql, hecha en Clojure...
Si les gusta Lisp, les va a encantar Clojure.
Para empezar podemos utilizar un entorno online. Uno que esta bueno es jdoodle que se encuentra en : https://www.jdoodle.com/execute-clojure-online/
Veamos un pequeño ejemplito :
(ns clojure.examples.hello
(:gen-class))
(defn sum-of-numbers [x y]
(println (format "x + y = %d" (+ x y))))
(sum-of-numbers 10 25)
Y esto retornará : x + y = 35
Dejo links : https://clojure.org/
https://steemit.com/utopian-io/@laxam/programming-in-clojure-part-1-why-clojure
Javascript es un lenguaje de tipado dinamico, lo que no quiere decir que no tenga tipos.
Encontre esta imagen en internet que muestra muy bien los tipos :
Null es raro porque es primitivo y tambien es un objeto, para mi no se decidian. Pero salvo eso esta muy bien la imagen.
En este informe se puede encontrar cosas como, lenguajes más usados, de que parte del mundo se commitea más, cantidad de commits, etc...
Ya hable de los lenguajes más utilizados en este post : https://emanuelpeg.blogspot.com/2020/12/top-10-de-los-lenguajes-de-programacion.html
Dejo link: https://octoverse.github.com/
Github liberó el resultado de una métrica que indica cual es el lenguaje más utilizado en su repositorio. Y el ganador por tercer año consecutivo es Javascript.
El que sorprendió fue typescript, recuperándose de un 2019 no tan bondadoso.
Y listo, no hay otras novedades...
No sé si el orden es importante, pero personalmente pienso que esta buena, las 3 primeras distros las use y estan muuuyyy buenas.
Sin más dejo link al post :
https://computerhoy.com/listas/software/mejores-distribuciones-linux-2018-75139
|
Es gratuito y esta realizado por la universidad de Glasgow.
Entre los capitulos tenemos, teoría, entrevistas, casos prácticos, etc...
Dejo link: https://www.futurelearn.com/courses/functional-programming-haskell
Las operaciones de secuencia se pueden clasificar en los siguientes grupos con respecto a sus requisitos de estados:
Si una operación de secuencia devuelve otra secuencia, que se produce de forma perezosa, se llama intermedia. De lo contrario, la operación es terminal. Ejemplos de operaciones de terminal son toList () o sum (). Los elementos de secuencia se pueden recuperar solo con operaciones de terminal.
Las secuencias se pueden iterar varias veces; sin embargo, algunas implementaciones de secuencia pueden limitarse a repetirse una sola vez. Eso se menciona específicamente en su documentación.
Echemos un vistazo a la diferencia entre Iterable y Sequence con un ejemplo.
Suponga que tiene una lista de palabras. El siguiente código filtra las palabras de más de tres caracteres e imprime la longitud de las primeras cuatro palabras.
val words = "The quick brown fox jumps over the lazy dog".split(" ")
val lengthsList = words.filter { println("filter: $it"); it.length > 3 }
.map { println("length: ${it.length}"); it.length }
.take(4)
println("Lengths of first 4 words longer than 3 chars:")
println(lengthsList)
Cuando ejecute este código, verá que las funciones filter () y map () se ejecutan en el mismo orden en que aparecen en el código. Primero, verá filter: para todos los elementos, luego length: para los elementos que quedan después de filtrar, y luego la salida de las dos últimas líneas. Así es como va el procesamiento de la lista:
Ahora escribamos lo mismo con las secuencias:
val words = "The quick brown fox jumps over the lazy dog".split(" ")
//convert the List to a Sequence
val wordsSequence = words.asSequence()
val lengthsSequence = wordsSequence.filter { println("filter: $it"); it.length > 3 }
.map { println("length: ${it.length}"); it.length }
.take(4)
println("Lengths of first 4 words longer than 3 chars")
// terminal operation: obtaining the result as a List
println(lengthsSequence.toList())
La salida de este código muestra que las funciones filter () y map () se llaman solo cuando se genera la lista de resultados. Entonces, primero verá la línea de texto “Longitudes de ..” y luego se iniciará el procesamiento de la secuencia. Para los elementos que quedan después del filtrado, el mapa se ejecuta antes de filtrar el siguiente elemento. Cuando el tamaño del resultado llega a 4, el procesamiento se detiene porque es el tamaño más grande posible es 4 por ".take(4)"