sábado, 2 de mayo de 2020

Listas en Haskell


Una estructura de datos clave es la lista

Sintaxis: los elementos se escriben entre corchetes, separados por comas.

['3', 'a']
[2.718, 50.0, -1.0]

Una función solo puede devolver un resultado pero las listas le permiten agrupar 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]

Ojo! se puede hacer pero no esta del todo bien...

Se puede escribir una lista constante, de la siguiente manera:

mylist = [2,4,6,8]

A la vez los elementos pueden ser expresiones y se evalúan solo cuando se usan. Supongamos que se define:

answer = 42
yourlist = [7, answer+1, 7*8]

Entonces

yourlist -- > [7, 43, 56]

Pero mientras no acceda a la expresión, no se evalúa.

El operador (++) toma dos listas existentes y retorna una nueva que contiene todos los elementos de las listas pasadas por parámetro.

[23, 29] ++ [48, 41, 44] -- > [23, 29, 48, 41, 44]

Si xs es una lista, entonces [] ++ xs = xs = xs ++ [].

A veces es útil tener una secuencia de números.

En notación matemática estándar, puedes escribir 0,1, ..., n.

Haskell tiene una notación de secuencia para listas.

La secuencia se escribe 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]

Las secuencias no están limitadas a números, hay muchos tipos enumerables donde hay una forma natural de incrementar un valor y se puede usar secuencias en cualquier de estos tipos.

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']

['0' ... '9'] -> ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ']

es una lista de caracteres (que resultan ser los caracteres de dígitos);

[0 .. 9] -> [0,1,2,3,4,5,6,7,8,9]
Es una lista de números.

Una comprensión de lista 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 del conjunto de notación matemática.

Ejemplos de comprensiones de conjuntos:

  • Un conjunto obtenido multiplicando 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]]

Podemos indexar una lista numerando los elementos, comenzando por 0. Así, 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 indefinido.

Hay funciones de la biblioteca estándar para acceder encabezado de una lista (su primer elemento) o la cola (todo el resto de la lista)

El resultado de aplicar head o tail a la lista vacía no está definido.

head :: [a] -> a
head [4,5,6] -- > 4
tail :: [a] -> [a]
tail [4,5,6] -- > [5,6]


Hemos mencionado antes que Haskell es "lazy", lo que significa que solo evalúa expresiones cuando son necesarias para la evaluación de otra expresión. Este comportamiento se extiende a las 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 primos (del paquete Data.Numbers.Primes) que devuelve una lista infinita de números primos. Una consecuencia de lazy en las listas es que puede definir listas que contengan expresiones muy complejas y que requieren mucho tiempo, y no se evaluarán, si no son requeridas. 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.

Otra cosa muy importante es que las listas también son inmutables. Como resultado, si define xs2 = xs ++ xs e intenta acceder al tercer elemento xs2 !! 2 aún generará un error porque xs no se ha modificado:

xs2 !! 2 -- > *** Exception: Prelude.(!!): index too large

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 mediante la reducción: el orden de las expresiones no importa.

PD: la imagen es de http://learnyouahaskell.com/ con licencia Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License  porque no pudo encontrar una licencia con un nombre más largo.

No hay comentarios.:

Publicar un comentario