Tutorial Haskell #4: Listas y enumeraciones

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]

Powered by Drupal, an open source content management system