Dejo link: https://tourofscala.com/
Translate
martes, 27 de octubre de 2020
Tour of Scala
domingo, 25 de octubre de 2020
Introducción al cálculo Lambda
El cálculo lambda fue desarrollado en la década de 1930 por Alonzo Church (1903-1995), uno de los principales desarrolladores de lógica matemática. El cálculo lambda fue un intento de formalizar funciones como medio de computación.
Un avance importante (realmente el mayor) en la teoría de la computabilidad fue la prueba de que el cálculo lambda y la máquina de Turing tienen exactamente el mismo poder computacional. Esto llevó a la tesis de Church: que el conjunto de funciones que son efectivamente computables es exactamente el conjunto computable por la máquina de Turing o el cálculo lambda.
La tesis se fortaleció cuando varios otros sistemas de computación matemática (Problema posterior a la correspondencia y otros) también demostraron ser equivalentes al cálculo lambda.
El punto es que el conjunto de funciones efectivamente computables parece ser una realidad fundamental, no solo una peculiaridad de cómo se definió la {máquina de Turing, cálculo lambda}.
El cálculo lambda ha resultado capturar dos aspectos de una función:
- Un objeto matemático (conjunto de pares ordenados de dominio y rango), y
- Una máquina de caja negra abstracta que toma una entrada y produce una salida.
El cálculo lambda es fundamental para la semántica denotacional, la teoría matemática de lo que significan los programas de computadora.
Los lenguajes de programación funcional se desarrollaron con el objetivo explícito de convertir el cálculo lambda en un lenguaje de programación práctico.
El compilador ghc Haskell opera (1) desechando el programa fuente, (2) transformando el programa en una versión del cálculo lambda llamado Sistema F, y (3) traduciendo el Sistema F al lenguaje de máquina usando reducción de grafos.
Trabajaremos con el cálculo lambda básico “enriquecido” con algunas constantes y funciones primitivas (estrictamente hablando, eso no es necesario).
El lenguaje tiene constantes, variables, aplicaciones y funciones.
exp = const
| var
| exp exp
| \ var -> exp
Cada aparición de una variable en una expresión está ligada o es libre.
En ∖x → x + 1, la ocurrencia de x en x + 1 está limitada por ∖x.
En y ∗ 3, la ocurrencia o y es libre. Debe definirse en otro lugar, quizás como una definición global.
En general, una aparición de una variable está vinculada si hay alguna expresión lambda adjunta que la vincula; si no hay enlace lambda, entonces la ocurrencia es libre.
Debemos tener cuidado: la primera aparición de a es libre pero la segunda aparición está vinculada.
a + (\ a -> 2 ^ a) 3 -> a + 2 ^ 3
Ser libre o vinculada es una propiedad de la ocurrencia de una variable, ¡no de la variable en sí!
La computación en el cálculo lambda se realiza utilizando tres reglas de conversión.
Las reglas de conversión le permiten reemplazar una expresión por otra ("igual").
Algunas conversiones simplifican una expresión; estos se llaman reducciones.
Conversión alfa : La conversión alfa le permite cambiar el nombre de un parámetro de función de manera consistente. ¡Pero no puede cambiar las variables libres con la conversión alfa!
La definición detallada de conversión alfa es un poco complicada, porque debe tener cuidado de ser coherente y evitar la "captura de nombres". No nos preocuparemos por los detalles en este momento.
(\ x -> x + 1) 3
(\ y -> y + 1) 3
Conversión beta : La conversión beta es el "caballo de batalla" del cálculo lambda: define cómo funcionan las funciones.
Para aplicar una expresión lambda a un argumento, se toma el cuerpo de la función y se reemplaza cada aparición enlazada de la variable con el argumento.
(\ x -> exp1) exp2se evalúa como exp1 [exp2 / x]
Ejemplo:
(\ x -> 2 * x + g x) 42
se evalúa como 2 ∗ 42 + g 42
Conversión ETA : Eta conversión dice que una función es equivalente a una expresión lambda que toma un argumento y aplica la función al argumento.
(\ x -> f x)
es equivalente a f
Ejemplo (recuerde que (∗ 3) es una función que multiplica su argumento por 3)
(\ x -> (* 3) x)
es equivalente a (∗ 3)
Intente aplicar ambos a 50:
(\ x -> (* 3) x) 50
es lo mismo que (∗ 3) 50
Existe un uso común de la conversión Eta. Supongamos que tenemos una definición como esta:
f x y = g y
Esto se puede reescribir de la siguiente manera:
f = \ x -> (\ y -> g y)
f = \ x -> g = f x = g
Por tanto, las siguientes dos definiciones son equivalentes:
f x y = g y
f x = g
En efecto, dado que el último argumento en ambos lados de la ecuación es el mismo (y), se puede “factorizar”.
Y listo, no quiero profundizar en detalles matemáticos, pero sin duda que esta teoría influyo mucho el mundo de los lenguajes y quiero compartir una imagen que encontré y describe como ha llegado esta teoría al mainstream de los lenguajes y gracias a quienes :
viernes, 23 de octubre de 2020
Cómo y por qué Twitter usa Scala ?
Twitter usa Scala, si! hace mucho. Scala es un lenguaje de programación que combina rasgos de lenguajes orientados a objetos. y lenguajes funcionales con miras a soportar mejor la concurrencia en software a gran escala.
¿Por qué utilizar Scala? Lo que necesitaba a medida que Twitter crecía era procesos pesados de larga ejecución, cola de mensajes, capas de almacenamiento en caché para realizar 20.000 operaciones por segundo.
¿cuáles fueron los criterios para elegir Scala? Bueno, primero ¿es rápido, divertido y bueno para un proceso de larga duración? ¿Tiene funciones avanzadas? ¿Puede ser productivo rápidamente? Los desarrolladores del lenguaje en sí tenían que ser accesibles para nosotros.
¿Y Scala resultó ser rápido? Bueno, ¿cuál es tu definición de rápido? Casi tan rápido como Java. No tiene que ser tan rápido como C o Assembly. Python no es significativamente más rápido que Ruby. Querían hacer más con menos máquinas, aprovechando mejor la concurrencia; quería que se compilara para que no quemara la CPU haciendo las cosas incorrectas.
Es muy divertido trabajar en Scala; sí, puede escribir código serio, similar a Java, cuando empiece. Más tarde, puede escribir código Scala que casi se parece a Haskell. Puede ser muy idiomático, muy funcional, hay mucha flexibilidad ahí.
Y es rápido. El principal desarrollador de lenguaje de Scala trabajó en la JVM en Sun. Cuando comenzó Java, era claramente un gran lenguaje, pero la máquina virtual era lenta. La JVM se ha llevado a la era moderna y no nos lo pensamos dos veces antes de usarla.
Scala puede tomar prestadas bibliotecas de las bibliotecas de Java; está compilando en código de bytes de Java, y todo está llamando de un modo que es realmente eficiente. No nos hemos encontrado con ninguna dependencia de biblioteca que cause problemas. Podemos contratar personas con Java y pueden hacerlo bastante bien.
La gran teoría unificada de Scala es que combina programación orientada a objetivos (OOP) y programación funcional (FP). El objetivo de Scala es esencialmente decir que OOP y FP no tienen que ser estos mundos separados. Es algo zen, y no lo entiendes cuando empiezas. Es realmente poderoso; es bueno tener un lenguaje con una tesis, en lugar de tratar de atraer a todos los programadores. Scala está tratando de resolver un problema intelectual específico.
Tiene métodos que toman cualquier cosa entre una cadena y varios puntos en la cadena de herencia de una cadena. La sintaxis es más flexible que la de Java; es muy legible por humanos, ya que puede omitir un período entre las llamadas al método para que parezca una serie de palabras. Su programa puede hacer buenas declaraciones declarativas sobre la lógica de lo que está tratando de hacer.
Con Scala, también puede usar rasgo o traits. Esto es útil porque, por supuesto, tiene preocupaciones transversales en su aplicación. Por ejemplo, todos los objetos deben poder registrar cosas, pero no quieres que todo se extienda desde una clase de registrador, eso es una locura. Con Scala, puede usar un rasgo para introducirlo directamente y puede agregar tantos rasgos como desee a una clase u objeto determinado.
Puedes elegir entre mutabilidad e inmutabilidad. Esto puede resultar peligroso. 9 de cada 10 veces usa variables inmutables cuando desea previsibilidad, especialmente cuando tiene cosas ejecutándose al mismo tiempo. Pero Scala confía en el programador para la mutabilidad cuando lo necesita.
Scala tiene el concepto de valores perezosos; se puede decir lazy val x = una función realmente complicada. Eso no se calculará hasta el último segundo, cuando necesite ese valor. Esto es bonito.
Pattern matching también es buena. Le permite sumergirse en una estructura de datos para que pueda, por ejemplo, explotar una colección que coincida con una matriz con "2" como tercer elemento. Puede dividir cadenas y expresiones regulares, y puede combinar grupos de patrones con expresiones regulares.
Una característica extraña que es realmente útil es la capacidad de usar literales XML, de modo que pueda hacer algo igual a un literal XML, como si el literal XML fuera una cadena. No tienes que importar Sax o alguna biblioteca XML.
Cuando la gente lee sobre Scala, casi siempre es en el contexto de la concurrencia. La simultaneidad la puede resolver un buen programador en muchos lenguajes, pero es un problema difícil de resolver. Scala tiene una biblioteca de Actores, Akka, que se usa comúnmente para resolver problemas de concurrencia, y hace que ese problema sea mucho más fácil de resolver.
Un actor es un objeto que tiene un buzón; pone en cola los mensajes y los trata en un bucle, y puede dejar un mensaje en el suelo cuando no sabe qué hacer con él.
Puede modelar la concurrencia como mensajes, una unidad de trabajo, enviados a los actores, lo cual es realmente bueno. Es como usar un sistema de cola. También puede usar Java.util.concurrency, Netty y Apache Mina, colocándolo directamente. Puede reescribir la implementación de Actor, y algunas personas han ido tan lejos como para lanzar sus propias bibliotecas de memoria transaccional de software.
La interoperabilidad de Java es una gran, gran victoria. Hay diez años de grandes bibliotecas, cosas como Jodatime. Usamos mucho Hadoop y ha sido fácil conectar Scala a las bibliotecas de Hadoop. Usamos Thrift, sin tener que parchearlo; utilizamos bibliotecas de Apache Commons y de Google.
En el mundo empresarial, una arquitectura orientada a servicios no es nueva, pero en la Web 2.0 es una ciencia nueva y loca. Con PHP o Ruby on Rails, cuando necesite más funcionalidad, simplemente incluya más complementos y bibliotecas, y los inserte todos en el servidor. El resultado es una bola de barro gigante.
Entonces, cualquier cosa que tenga que hacer un trabajo pesado en nuestra pila será un servicio independiente. Podemos probarlo de forma independiente, es una buena forma de descomponer nuestra arquitectura.
¿Qué servicios de Twitter funcionan con Scala? Tienen un sistema de colas llamado Kestrel. Utiliza una versión mejorada del protocolo mem-cache. Originalmente se escribio en Ruby, pero debido a que Ruby es un lenguaje dinámico, el servicio comenzó a mostrar sus puntos débiles de rendimiento.
Flock usan para almacenar grafos social, como una lista desnormalizada de identificadores de usuario. No es una base de datos de grafos, por lo que no puede realizar recorridos aleatorios a lo largo del grafo. Pero es excelente para almacenar rápidamente conjuntos desnormalizados de ID de usuario y hacer intersecciones. Estan realizando 20.000 operaciones por segundo en ese momento, respaldadas por un esquema MySQL diseñado para mantener tanto como sea posible en la memoria. Ha sido muy eficiente, no se necesitan muchos servidores.
El servicio de búsqueda de personas está impulsada por un servicio creado por Scala que se llama Hawkwind. Es un grupo de objetos de usuario que Hadoop arroja, donde la solicitud se distribuye en varias máquinas y luego se vuelve a unir.
miércoles, 21 de octubre de 2020
Quien utiliza Haskell??
La programación funcional (en particular Haskell) es utilizada por un número creciente de empresas en la actualidad, incluidas algunas grandes multinacionales.
Hay listas de empresas que utilizan Haskell en el sitio web de Haskell y en Quora. Algunas companias destacadas son Facebook, IBM, Twitter, AT&T, Bank of America, Barclays Capital, NVIDIA y Microsoft.
Facebook utiliza Haskell en varios proyectos, por ejemplo Lucha contra el spam con Haskell.
Galois ha sido un usuario y promotor activo de Haskell durante más de una década. Esta charla presenta tres estudios de caso de algunos de sus proyectos.
The New York Times tiene una agradable charla sobre Haskell en la sala de redacción.
lunes, 19 de octubre de 2020
QuickCheck: prueba automática programas Haskell
QuickCheck es una biblioteca para pruebas aleatorias de propiedades de programas.
El programador proporciona una especificación del programa, en forma de propiedades que las funciones deben satisfacer, y QuickCheck luego prueba que las propiedades se cumplen en una gran cantidad de casos generados aleatoriamente.
Las especificaciones se expresan en Haskell, utilizando combinadores proporcionados por QuickCheck.
QuickCheck proporciona combinadores para definir propiedades, observar la distribución de los datos de prueba y definir generadores de datos de prueba.
veamos un ejemplo, primero definamos una propiedad :
prop_RevRev xs = reverse (reverse xs) == xs
where types = xs::[Int]
y luego a probar la propiedad
Main> quickCheck prop_RevRev
OK, passed 100 tests.
Y si fallará nos mostraría todas las veces que fallo :
Main> quickCheck prop_RevId
Falsifiable, after 1 tests:
[-3,15]
Y listo, tenemos una comprobación rapida y fácil para nuestros programas.
Dejo link: http://www.cse.chalmers.se/~rjmh/QuickCheck/
domingo, 18 de octubre de 2020
Currying en Haskell
Si tenemos esta firma de función :
f :: X -> Y -> Z -> A
La flecha "->" es asociativa a la derecha, por lo que es lo mismo que:
f :: X -> (Y -> (Z -> A))
Lo que esto significa es que podemos considerar f como una función con un solo argumento de tipo X que devuelve una función de tipo Y-> Z-> A.
La técnica de reescribir una función de múltiples argumentos en una secuencia de funciones con un solo argumento se llama currización. Podemos ilustrar esto mejor usando una función lambda:
\ x y z -> ...
\ x -> (\ y z -> ...)
\ x -> (\ y -> (\ z -> ...))
El nombre "curry", es una referencia al lógico Haskell Curry. El concepto en realidad fue propuesto originalmente por otro lógico, Moses Schönfinkel, pero su nombre no era tan pegadizo.
La aplicación parcial significa que no necesitamos proporcionar todos los argumentos a una función, para aplicar la función. Por ejemplo, dado
sq x y = x*x+y*y
Observamos que la aplicación de la función se asocia a la izquierda, por lo que se cumple la siguiente equivalencia
sq x y = (sq x) y
Por tanto, podemos crear una función especializada mediante la aplicación parcial de x:
sq4 = sq 4 -- = \y -> 16+y*y
sq4 3 -- = (sq 4) 3 = sq 4 3 = 25
Es por eso que puedes escribir cosas como:
dobles = mapa (* 2) [1 ..]
Sequencias en Kotlin
Leyendo sobre estructuras infinitas en Haskell, me puse a pensar en las secuencias en Kotlin. La biblioteca estándar de Kotlin contiene un tipo de contenedor: secuencias (Sequence <T>). Las secuencias ofrecen las mismas funciones que Iterable pero implementan otro enfoque un enfoque perezoso.
Cuando el procesamiento de un Iterable incluye varios pasos, se ejecutan de forma ansiosa o eager : cada paso de procesamiento completa y devuelve su resultado: una colección intermedia. El siguiente paso se ejecuta en esta colección. Pero, el procesamiento de múltiples pasos de secuencias se ejecuta de manera perezosa cuando es posible: la computación real ocurre solo cuando se solicita el resultado de toda la cadena de procesamiento.
El orden de ejecución de las operaciones también es diferente: la secuencia realiza todos los pasos de procesamiento uno por uno para cada elemento. Pero, Iterable completa cada paso para toda la colección y luego pasa al siguiente paso.
Por lo tanto, las secuencias nos permiten evitar generar resultados de pasos intermedios, mejorando así el rendimiento de toda la cadena de procesamiento de la colección. Sin embargo, la naturaleza perezosa de las secuencias agrega algunos gastos generales que pueden ser significativos al procesar colecciones más pequeñas o al realizar cálculos más simples. Por lo tanto, debe considerar tanto Sequence como Iterable y decidir cuál es mejor para cada caso.
Para crear una secuencia, llammamos a la función sequenceOf () que enumera los elementos como sus argumentos.
val nroSecuencia = sequenceOf ("cuatro", "tres", "dos", "uno")
Si ya tiene un objeto Iterable (como una Lista o un Conjunto), puede crear una secuencia a partir de él llamando a asSequence ().
val numeros = listOf ("uno", "dos", "tres", "cuatro")
val numbersSequence = numeros.asSequence()
Una forma más de crear una secuencia es construyéndola con una función que calcula sus elementos. Para construir una secuencia basada en una función, llame a generateSequence () con esta función como argumento. Opcionalmente, puede especificar el primer elemento como un valor explícito o como resultado de una llamada de función. La generación de la secuencia se detiene cuando la función proporcionada devuelve un valor nulo. Por lo tanto, la secuencia del siguiente ejemplo es infinita:
val oddNumbers = generateSequence(1) { it + 2 } // `it` is the previous element
println(oddNumbers.take(5).toList())
//println(oddNumbers.count()) // error: the sequence is infinite
Para crear una secuencia finita con generateSequence(), proporcione una función que devuelva nulo después del último elemento que necesita.
val oddNumbersLessThan10 = generateSequence(1) { if (it + 2 < 10) it + 2 else null }
println(oddNumbersLessThan10.count())
Finalmente, hay una función que le permite producir elementos de secuencia uno por uno o por trozos de tamaños arbitrarios: la función secuencia (). Esta función toma una expresión lambda que contiene llamadas de las funciones yield () y yieldAll (). Devuelven un elemento al consumidor de secuencia y suspenden la ejecución de secuencia () hasta que el consumidor solicite el siguiente elemento. yield () toma un solo elemento como argumento; yieldAll () puede tomar un objeto Iterable, un Iterador u otra Secuencia. Un argumento Sequence de yieldAll () puede ser infinito. Sin embargo, dicha llamada debe ser la última: todas las llamadas posteriores nunca se ejecutarán.
Veamos un ejemplo:
val oddNumbers = secuencia {
yield (1)
yieldAll (listaDe (3, 5))
yieldAll (generateSequence (7) {it + 2})
}
println (OddNumbers.take (5) .toList ())
Veamos la secuencia de fibonacci :
fun fibonacci(): Sequence<Int> = sequence {
var fibo = Pair(0, 1)
while (true) {
yield(fibo.first)
fibo = Pair(fibo.second, fibo.first + fibo.second)
}
}
fun main(args: Array<String>) {
fibonacci().take(4).toList().toString() eq "[0, 1, 1, 2]"
fibonacci().take(10).toList().toString() eq "[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]"
}
Dejo link : https://kotlinlang.org/docs/reference/sequences.html
sábado, 17 de octubre de 2020
Estructuras de datos infinitas
En el post anterior, hablamos listas infinitas. Podemos definir una lista infinita de enteros consecutivos de la siguiente manera:
[1 ..]
Podemos evaluar esta lista, pero no se imprimirá en su totalidad.
Debemos utilizar las funciones de take y drop para trabajar con listas infinitas. Esto permite realizar una cantidad finita de procesamiento en una lista infinita, pero no recorrerla infinitamente.
La razón por la que Haskell puede procesar listas infinitas es porque evalúa las listas de manera perezosa, es decir, solo evalúa los elementos de la lista cuando son necesarios.
Ahora echemos un vistazo a dos listas de enteros conocidas. Estudiaremos sus definiciones recursivas.
Números de Fibonacci : El n-ésimo número de Fibonacci es la suma de los dos números de Fibonacci anteriores. Los dos primeros números son ambos 1. Luego el tercero es 2, seguido de 3, 5, etc.
1, 1, 2, 3, 5, 8, 13, 21, ...
Esto se puede expresar en Haskell usando la función zipWith, combinando pares de elementos con el operador de suma.
let fibs = 1: 1: (zipWith (+) fibs (tail fibs))
Podemos evaluar elementos individuales de esta lista usando el !! y el indice. O podríamos tomar los primeros n elementos de la lista de fibs.
Números primos: A continuación se muestra una serie de expresiones de filtro para calcular una lista infinita de números primos.
properfactors :: Int -> [Int]
properfactors x = filter (\y->(x `mod` y == 0)) [2..(x-1)]
numproperfactors :: Int -> Int
numproperfactors x = length (properfactors x)
primes :: [Int]
primes = filter (\x-> (numproperfactors x == 0)) [2..]
La función de factores propios toma un valor entero x y devuelve una lista de factores propios para x. Los factores son números que dividen x y no dejan resto. Los factores adecuados para un número entero x no incluyen 1 ni x.
La función numproperfactors simplemente cuenta cuántos factores propios hay para x, devolviendo la longitud de la lista x de factores propios.
Finalmente, la lista de primos usa la función de filtro para seleccionar enteros x que no tienen factores en el rango de 2 a (x-1) inclusive.
Operando con listas en Haskell
Podemos indexar una lista numerando los elementos, comenzando con 0. Por tanto, una forma canónica de una lista con n elementos es [x0, x1, .. xn − 1].
El operador !! toma una lista y un índice y devuelve el elemento correspondiente.
[5,3,8,7] !! 2 -> 8
[0 .. 100] !! 81 -> 81
['a' .. 'z'] !! 13 -> 'n'
Si el índice es negativo o demasiado grande, se devuelve undefined.
Para una programación robusta, debemos asegurarnos de que todas las expresiones estén bien definidas o de que todas las excepciones sean capturadas y manejadas.
Hay funciones de biblioteca estándar para obtener primer elemento (head) o todo el resto de la lista (tail).
El resultado de aplicar head o tail a la lista vacía es indefinido
head :: [a] -> a
head [4,5,6] -> 4
tail :: [a] -> [a]
tail [4,5,6] -> [5,6]
Como recomendación: debemos evitar usar (head) y (tail), para evitar valores indefinidos. Se puede utilizar pattern matching que es más robusto.
Las listas en haskell son perezosas, hemos mencionado antes que Haskell es "perezoso", lo que significa que solo evalúa expresiones cuando son requeridas para la evaluación de otra expresión. Este comportamiento se extiende a listas, por lo que podemos definir listas infinitas usando secuencias, por ejemplo [1 .. ] es la lista de todos los enteros positivos. Otro ejemplo es la función primes (del paquete Data.Numbers.Primes) que devuelve una lista infinita de números primos. Una consecuencia de la pereza en las listas es que puede definir listas que contengan expresiones muy complejas y que consuman mucho tiempo, y siempre que nunca acceda a ellas, no se evaluarán. Lo mismo es cierto para una expresión incorrecta, por ejemplo, definir xs = [1,2, xs !! 5,4] no generará un error siempre que no acceda al tercer elemento.
Las listas también son inmutables. Como resultado, si definimos xs2 = xs ++ xs e intenta acceder al tercer elemento xs2 !! 2 seguirá dando como resultado un error porque xs no se ha modificado.
Curiosamente, si cambiamos la definición de xs a xs = [1,2, xs2 !! 5,4], entonces ambas xs !! 2 y xs2 !! 2 devolverá 2:
xs = [1,2, xs2 !! 5,4]
xs2 = xs ++ xs
xs2 !! 2 -> 2
xs !! 2 -> 2
Esta es una consecuencia de la evaluación de expresiones de Haskell a través de la reducción: el orden de las expresiones no importa.
martes, 13 de octubre de 2020
Lista de comprensión en Haskell
Una lista de comprensión es una notación de alto nivel para especificar el cálculo de una lista.
El compilador transforma automáticamente las comprensiones de una lista en una expresión utilizando una familia de funciones básicas que operan en listas.
Las comprensiones de listas se inspiraron en la comprensión de conjuntos de notación matemática.
Ejemplos de comprensiones de conjuntos:
Un conjunto obtenido al multiplicar los elementos de otro conjunto por 3 es {3 × x | x ← {1,…, 10}}.
El conjunto de números pares es {2 × x | x ← N}.
El conjunto de números impares es
{2 × x + 1 | x ← N}.
El producto cruzado de dos conjuntos A y B es {(a, b) | a ← A, b ← B}.
Ejemplos de listas por comprensión
[3 * x | x <- [1..10]]
-> [3,6,9,12,15,18,21,24,27,30]
[2 * x | x <- [0..10]]
-> [0,2,4,6,8,10,12,14,16,18,20]
[2 * x + 1 | x <- [0..10]]
-> [1,3,5,7,9,11,13,15,17,19,21]
[[a, b] | a <- [10,11,12], b <- [20,21]]
-> [[10,20], [10,21], [11,20], [11,21], [12,20], [12,21]]
lunes, 12 de octubre de 2020
Virtualenv de Python
¿qué es virtualenv? Virtualenv es una herramienta que nos permite realizar entornos python aislados. Imagine que tiene una aplicación que necesita la versión 2 de una biblioteca, pero otra aplicación requiere la versión 3. ¿Cómo puede utilizar y desarrollar ambas aplicaciones?
Si instala todo en /usr/lib/python2.7/site-packages (o cualquiera que sea la ubicación estándar de su plataforma), es fácil terminar en una situación en la que involuntariamente actualice un paquete.
En otro caso, imagina que tienes una aplicación que está completamente desarrollada y no quieres hacer ningún cambio en las bibliotecas que está usando, pero al mismo tiempo comienzas a desarrollar otra aplicación que requiere las versiones actualizadas de esas bibliotecas.
¿Qué harás? ¡Usa virtualenv! Crea entornos aislados para su aplicación de Python y esto nos permitirá instalar bibliotecas de Python en ese entorno aislado en lugar de instalarlas globalmente.
Para instalarlo, simplemente escriba este comando en el shell:
$ pip install virtualenv
Los comandos más importantes son:
$ virtualenv myproject
$ source myproject/bin/activat
Este primero crea un entorno virtualenv aislado en la carpeta myproject y el segundo comando activa ese entorno aislado.
Mientras crea el virtualenv, debe tomar una decisión. ¿Quiere que este virtualenv use paquetes de los paquetes de sitio de su sistema o los instale en los paquetes de sitio del virtualenv? De forma predeterminada, virtualenv no dará acceso a los paquetes globales del sitio.
Si desea que virtualenv tenga acceso a los paquetes de sistema, tenemos que activar --system-site-packages al crear su virtualenv de esta manera:
$ virtualenv --system-site-packages mycoolproject
Y Virtualenv se puede desactivar con :
$ deactivate
Ejecutar Python después de la desactivación usará la instalación de Python del sistema.
Puede usar smartcd, que es una biblioteca para bash y zsh y le permite alterar su entorno bash (o zsh) mientras hace cd. Puede ser muy útil activar y desactivar un virtualenv cuando cambia de directorio.
sábado, 10 de octubre de 2020
Secuencias en Haskell
A veces es útil tener una secuencia de números. En notación matemática estándar, puede escribir 0,1,…, n.
Haskell tiene una notación de secuencia para listas.
Escriba la secuencia entre corchetes, con el valor inicial, el operador .. y el valor final.
[0 .. 5] -> [0,1,2,3,4,5]
[100 .. 103] -> [100,101,102,103]
Los elementos se incrementan en 1 y las secuencias no se limitan a números
Hay muchos tipos enumerables en los que existe una forma natural de incrementar un valor.
Puede utilizar secuencias en cualquier tipo.
Los caracteres son enumerables y se pueden expresar listas de esta manerá.
Por ejemplo:
[’A’ .. ’z’]
-> ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', ' m ',' n ',' o ',' p ',' q ',' r ',' s ',' t ',' u ',' v ',' w ',' x ',' y ' , 'z']
es una lista de caracteres;
[’0’ .. ’9’]
-> [’0’, ’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, ’8 ',' 9’]
es una lista de caracteres (que resultan ser los dígitos);
[0 .. 9]
-> [0,1,2,3,4,5,6,7,8,9]
es una lista de números.
Listas en Haskell
Una estructura de datos clave en todo lenguaje es la lista
Una lista es un valor único que contiene varios otros valores.
En haskell una lista esta representada por elementos se escriben entre paréntesis cuadrados, separados por comas.
['3', 'a']
[2.718, 50.0, -1.0]
Una función solo puede devolver un resultado. Sin embargo, las listas le permiten empaquetar varios valores en un objeto, que puede ser devuelto por una función.
Aquí hay una función (minmax) que devuelve el menor y el mayor de dos números:
minmax = \ x y -> [min x y, max x y]
minmax 3 8 -> [3,8]
minmax 8 3 -> [3,8]
Los elementos de una lista se evalúan perezosamente, de esta forma se pueden hacer listas infinitas.
Puedes escribir una lista constante
mylist = [2,4,6,8]
Los elementos pueden ser expresiones y se evalúan solo cuando se utilizan. Suponga que define:
respuesta = 42
lista = [7, respuesta + 1, 7 * 8]
Si imprimimos esto :
lista -> [7, 43, 56]
Pero mientras no se acceda a la expresión, no se evaluará.
El operador (++) toma dos listas existentes y retorna una nueva que contiene todos los elementos.
El operador se pronuncia append y se escribe como dos caracteres + consecutivos.
[23, 29] ++ [48, 41, 44] -> [23, 29, 48, 41, 44]
La longitud del resultado es siempre la suma de las longitudes de las listas originales.
Si xs es una lista, entonces [] ++ xs = xs = xs ++ [].
viernes, 9 de octubre de 2020
Enumeraciones en Python
Enumerate es una funcionalidad incorporada de Python. Nos permite recorrer algo y tener un contador automático. Aquí hay un ejemplo:
my_list = ['apple', 'banana', 'grapes', 'pear']
for counter, value in enumerate(my_list):print counter, value
# Output:
# 0 apple
# 1 banana
# 2 grapes
# 3 pear
¡Y hay más! enumerate también acepta un argumento opcional que nos permite especificar el índice de inicio del contador.
my_list = ['apple', 'banana', 'grapes', 'pear']
for c, value in enumerate(my_list, 1):
print(c, value)
# Output:
# 1 apple
# 2 banana
# 3 grapes
# 4 pear
Un ejemplo de dónde resulta útil el argumento opcional de enumerate es la creación de tuplas que contienen el índice y el elemento de lista mediante una lista. Aquí hay un ejemplo:
my_list = ['apple', 'banana', 'grapes', 'pear']
counter_list = list(enumerate(my_list, 1))
print(counter_list)
# Output: [(1, 'apple'), (2, 'banana'), (3, 'grapes'), (4, 'pear')]
Dejo link : https://book.pythontips.com/en/latest/enumerate.html
jueves, 8 de octubre de 2020
Funciones en Haskell
Haskell es un lenguaje funcional, por lo que el concepto de función es esencial para el lenguaje. Una función toma uno o más argumentos y calcula un resultado. Dados los mismos argumentos, el resultado siempre será el mismo. Esto es similar a una función matemática y significa que en Haskell no hay efectos secundarios. Hay dos operaciones fundamentales en las funciones: definición de función (creando una función) y aplicación de función (usando una función para calcular un resultado).
En Haskell, muchas funciones están predefinidas en una biblioteca estándar llamada preludio.
¡Pero la esencia de la programación funcional es definir sus propias funciones para resolver sus problemas!
Una función se define mediante una ecuación.
f = \ x -> x + 1 - función lambda
- o
f x = x + 1 - función nombrada
Esto es equivalente af (x) = x + 1 en notación matemática.
El lado izquierdo de la ecuación parece una variable, y eso es lo que es
El lado derecho es una expresión que usa las variables locales listadas entre paréntesis y define el resultado de la expresión.
Una definición de función es una ecuación, p. Ej. f = ∖ x → x + 1
El lado izquierdo da el nombre de la función;
El lado derecho (el "cuerpo") es una expresión que da los parámetros formales y el valor de la aplicación. La expresión puede usar los parámetros.
Una aplicación es una expresión como f 31, donde 31 es el argumento.
La aplicación se evalúa reemplazándola con el cuerpo de la función, donde los parámetros formales son reemplazados por los argumentos.
Ejemplo de aplicacion
f = \ x -> x + 1
f 3
-> {vincular x = 3}
(x + 1) donde x = 3
-> {sustituye 3 por x}
3 + 1
->
4
Podríamos tener funciones con multiples argumentos, una función con tres argumentos:
add3nums = \ x y z -> x + y + z
Para usarlo,
10 + 4 * add3nums 1 2 3
10 + (4 * (add3nums 1 2 3))
->
10 + (4 * (1 + 2 + 3))
->
10 + (4 * 6)
->
10 + 24
->
34