Cuando se quiere aplicar la misma operación a más de un valor se puede utilizar una lista para almacenar los diferentes argumentos, y también el resultado.
La forma inmediata de construir una lista es por medio de otra lista, que puede ser la lista vacía [], y el operador (:):
Prelude> :t [] [] :: [a] Prelude> :t (:) (:) :: a -> [a] -> [a] Prelude> 7:[] [7] Prelude> 3:it [3,7]
Una vez más, it representa al resultado inmediatamente anterior.
En la práctica esto solamente es emplea para agregar un elemento al principio de una lista o para escribir una función que construya listas. La forma general de construir listas es por medio de enumeraciones.
Se puede enumerar a partir de tipos en la clase de tipos Enum, que significa que todo valor de ese tipo (excepto los límites inferior y superior, si existen) tiene definido exactamente un predecesor y un sucesor.
Los límites de la enumeración, si existen, se pueden ver con minBound y maxBound, que no toman argumentos y regresan un valor que deben ser forzado a ser de un tipo en la clase de tipos Bounded (tipos que tienen un elemento mínimo y uno máximo):
Prelude> minBound :: Int -2147483648 Prelude> maxBound :: Int 2147483647 Prelude> minBound :: Char '\NUL' Prelude> maxBound :: Char '\1114111' Prelude> minBound :: Ordering LT Prelude> maxBound :: Ordering GT Prelude> minBound :: Integer <interactive>:1:0: No instance for (Bounded Integer) arising from a use of `minBound' at <interactive>:1:0-7 Possible fix: add an instance declaration for (Bounded Integer) In the expression: minBound :: Integer In the definition of `it': it = minBound :: Integer
Vemos que el tipo Int, siendo el tipo de enteros con signo de 32 bit, tiene límites, pero el tipo Integer, que representa abstractamente a los enteros, no.
Las funciones de enumeración en la biblioteca base de Haskell son:
succ devuelve al sucesor del argumento.
Prelude> succ 5 6 Prelude> succ 'd' 'e' Prelude> succ LT EQ Prelude> succ GT *** Exception: Prelude.Enum.Ordering.succ: bad argument
Como vimos antes, LT < EQ < GT, pero ya no sigue nada de GT, por eso la excepción.
pred devuelve al predecesor del elemento.
Prelude> pred 5 4 Prelude> pred 'd' 'c' Prelude> pred LT *** Exception: Prelude.Enum.Ordering.pred: bad argument Prelude> pred GT EQ
fromEnum regresa el índice del argumento en la enumeración "natural" que corresponde a su tipo; eso es, empieza a contar a partir del elemento que no tiene predecesor, dandole índice 0.
Prelude> fromEnum 'c' 99 Prelude> fromEnum LT 0 Prelude> fromEnum 45 45 Prelude> pred 0 -1 Prelude> fromEnum (-1) -1
Si no existe elemento que no tenga predecesor, como es el caso de los números enteros, empieza a contar a partir de su "cero" y le da índices negativos a sus predecesores.
toEnum hace lo opuesto; dado un índice, regresa el elemento en la enumeración de ese tipo que tenga ese índice. Si no existe tal elemento lanza una excepción.
Prelude> toEnum 65 <interactive>:1:0: Ambiguous type variable `a' in the constraint: `Enum a' arising from a use of `toEnum' at <interactive>:1:0-8 Probable fix: add a type signature that fixes these type variable(s) Prelude> toEnum 65 :: Char 'A' Prelude> toEnum 2 :: Ordering GT Prelude> toEnum 17 :: Int 17 Prelude> toEnum 3 :: Ordering *** Exception: Prelude.Enum.Ordering.toEnum: bad argument
Al usarlo desde el intérprete se debe indicar explícitamente el tipo del elemento, porque si no se hace se produce ambiguedad. En la práctica se puede emplear en contextos donde el tipo ya ha sido indicado o puede ser inferido.
Después de esta introducción, a continuación se mencionan las funciones que se emplean para construir listas.
enumFromTo parte del primer argumento y agrega todos los sucesores hasta llegar al segundo argumento.
enumFromThenTohace lo mismo, pero en lugar de agregar todos los sucesores salta del primer argumento al segundo, y después agrega sucesores en el mismo intervalo entre los argumentos; el tercer argumento es el límite.
Prelude> enumFromThenTo 2 7 99 [2,7,12,17,22,27,32,37,42,47,52,57,62,67,72,77,82,87,92,97]
enumFrom y enumFromTo hacen lo mismo que EnumFromTo, pero no tiene valor límite, y crea una lista infinita.
En el siguiente tutorial se mencionarán formas de tratar con listas infinitas; por ahora, para que deje de producir valores se puede cancelar con ^C (las teclas Ctrl y c, presionadas simultáneamente).
Prelude> enumFrom 4 [4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19^CInterrupted. Prelude> enumFromThen 3 7 [3,7,11,15,19,23,27,31,35,39,43,47,51^CInterrupted.
Por último, en la práctica nada de esto se usa excepto para construir funciones. La notación [desde..hasta] es equivalente a 'enumFromTo desde hasta', [desde,por..hasta] a 'enumFromThenTo desde por hasta', [desde..] a 'enumFrom desde' y [desde,por..hasta] a 'enumFromTo desde por'.
[codeblock lang=haskell]
Prelude> [3..10]
[3,4,5,6,7,8,9,10]
Prelude> [3,6..10]
[3,6,9]
Prelude> [3..]
[3,4,5,6,7,8,9,10,11,12,13,14,15^CInterrupted.
Prelude> [3,6..]
[3,6,9,12,15,18,21,24,27,30,33,36,39,42,45,48^CInterrupted.
[/codeblock]
