Introducción a Haskell: Lenguaje Funcional
Introducción a Haskell: Lenguaje Funcional
4 Indice
Desarrollo Temático
El
lenguaje
funcional
Haskell
mantiene
un
estándar
que
se
ha
venido
desarrollando
a
través
En
Haskel,
las
listas
de
números
o
de
elementos
que
se
pueden
enumerar
se
abrevian;
de
de
los
años.
El
más
conocido
ha
sido
el
Haskell
98,
pero
el
estándar
actual
es
el
del
2010.
En
el
manera
que
el
ejemplo
anterior
sería:
mundo
de
Haskell
existen
varias
implementaciones
del
lenguaje,
por
ejemplo
Hugs
(Haskell
User’s
Gofer
System),
Yhc
(York
Haskell
Compiler),
UHC
(Utrecht
Haskell
Compiler),
GHC
>
[1..5]
(Glasgow
Haskell
Compiler),
etc.
Unos
de
los
más
conocidos
son
Hugs
y
GHC.
En
esta
[1,2,3,4,5]
presentación
se
recomienda
la
utilización
del
Haskell
Platform,
que
contiene
un
paquete
de
herramientas
que
incluyen
el
compilador,
el
intérprete
de
GHC
y
un
conjunto
de
librerías
Acá,
se
encuentran
predefinidas
un
gran
conjunto
de
funciones
que
operan
sobre
las
listas;
estables
de
listas
para
que
se
pueda
utilizar.
por
ejemplo,
se
puede
sumar
los
elementos:
15
2
[ POLITÉCNICO GRANCOLOMBIANO] [ PARADIGMAS DE LA PROGRAMACIÓN ] 3
Multiplicarlos:
La
función
take
n
xs,
toma
los
primeros
n
elementos
de
la
lista
xs,
y
la
función
drop
n
xs
Contenido
elimina
los
primeros
n
elementos
de
una
lista.
>
product
[1..5]
>
take
5
[’a’..’i’]
120
"abcde"
Obtener
el
máximo
valor:
>
drop
5
[’a’..’i’]
Anterior
>
maximum
[1..5]
"fghi"
5
O
el
mínimo:
Haskell
proporciona
una
notación
para
listas,
que
a
la
vez
es
muy
práctica
y
expresiva:
las
Siguiente
>
minimum
[1..5]
listas
por
comprensión.
1
Al
recordar
la
teoría
de
conjuntos
escolar,
estos
se
pueden
denotar
por
extensión
y
por
Igualmente,
también
se
puede
obtener
la
longitud:
comprensión.
Cuando
se
nota
un
conjunto
por
extensión
se
listan
todos
sus
elementos,
por
ejemplo
{1,9,25}
;
mientras
que,
cuando
se
nota
un
conjunto
por
comprensión,
se
dan
las
>
length
[’a’..’z’]
características
o
reglas
de
formación:
{
x^2
|
x
∈
{1..5},
impar(x)
}.
Así,
se
pueden
crear
listas
por
comprensión
como
las
siguientes:
26
>
[
x^2
|
x<-‐[1..5],
odd
x
]
O
un
elemento
en
una
posición
determinada
(la
primera
posición
se
cuenta
desde
0):
[1,9,25]
>
[’a’..’z’]!!3
Para
denotar
la
lista
de
los
números
impares
entre
1
y
5
elevados
al
cuadrado,
a
diferencia
de
’d’
los
conjuntos,
importa
el
orden
y
se
admiten
elementos
repetidos;
en
este
sentido,
las
listas
Por
otra
parte,
se
pueden
obtener
el
primer
elemento:
son
más
parecidas
a
los
arreglos
que
a
los
conjuntos:
’a’ [25,9,1,1,9,25]
O
los
elementos,
excepto
el
primero:
En
este
último
ejemplo,
noten
que
el
orden
en
que
se
encuentran
los
elementos
corresponden
al
orden
en
que
se
tomaron
los
elementos
de
la
lista
[-‐5..5]
y
la
aparición
de
los
>
tail
[’a’..’i’]
elementos
repetidos.
"bcdefghi"
En
Haskell,
una
lista
de
caracteres
es
equivalente
a
una
cadena
de
caracteres
o
en
inglés
12.3. Programas
o
guiones
String,
es
por
esta
razón
que,
en
la
salida,
coloca
las
letras
encerradas
entre
comillas
y
no
con
la
notación
de
listas:
[‘b’,’c’,’d’,’e’,’f’,’g’,’h’,’i’],
no
obstante,
son
equivalentes:
Un
programa
o
guión
(script
en
inglés)
en
Haskell
es
un
archivo
en
formato
texto
que
contiene
un
conjunto
de
definiciones.
Para
caracterizar
que
un
archivo
es
un
programa
>
[’a’..’i’]
escrito
en
Haskell
se
utiliza
la
extensión
.hs.
"abcdefghi"
Por
ejemplo,
el
siguiente
texto
representa
un
programa
en
Haskell:
4
[ POLITÉCNICO GRANCOLOMBIANO] [ PARADIGMAS DE LA PROGRAMACIÓN ] 5
factorial
n
=
product
[1..n]
Si
todo
resulta
bien,
se
obtiene
el
mensaje
anterior,
de
lo
contrario
hay
que
revisar
que
el
Contenido
archivo
se
encuentre
en
el
mismo
directorio
donde
se
está
ejecutando
el
interpretador
y
que
promedio
xs
=
sum
xs
`div`
length
xs
no
contenga
errores
de
digitación.
La
primera
línea
del
archivo
está
definiendo
una
función
factorial
que
recibe
como
Una
vez
cargado
el
programa
se
pueden
utilizar
las
funciones
definidas
en
él,
por
ejemplo,
argumento
un
número
n
y
como
resultado
retorna
la
multiplicación
de
los
números
entre
1
y
calcular
el
factorial
de
un
número:
n.
En
este
caso,
los
nombres
de
las
variables
y
de
las
funciones
comienzan
por
letras
en
Anterior
minúscula,
mientras
que
los
nombres
de
las
constantes
como
True
y
False
comienzan
por
>
factorial
10
letras
en
mayúscula.
3628800
Note
que
en
Haskell
no
es
necesaria
la
utilización
de
paréntesis
para
los
argumentos
simples;
O
el
promedio
(entero)
de
una
lista
de
datos:
sin
embargo,
si
el
argumento
es
una
expresión
más
compleja
que
un
único
argumento,
es
necesario
que
se
encuentre
entre
paréntesis.
Por
esta
razón
la
definición
de
factorial
se
hizo
>
promedio
[7,11,15,3,8]
Siguiente
como
factorial
n
y
no
como
factorial(n).
8
La
segunda
línea
define
el
promedio
entero
(el
promedio
como
un
número
entero,
es
decir,
eliminando
los
decimales
del
número)
de
una
lista
de
números
xs.
Para
calcularlo,
suma
12.4. Librerías
todos
los
elementos
de
la
lista
y
hace
la
división
entera
(retorna
el
cociente
de
la
división,
es
decir,
obvia
los
decimales)
entre
el
número
de
los
elementos
que
contiene
la
lista.
La
función
Dentro
de
los
scripts
se
puede
importar
definiciones
que
han
sido
realizadas
previamente
y
div
n
m
retorna
el
cociente
de
la
división
de
n
entre
m,
por
ejemplo:
recolectadas
en
librerías.
Por
ejemplo,
se
pueden
importar
otras
funciones
para
trabajar
con
listas
que
se
encuentran
definidas
en
la
librería
[Link]
y
funciones
para
trabajar
con
>
div
7
2
caracteres
[Link].
Del
mismo
modo,
se
puede
hacer
un
programa
con
funciones
que
conviertan
cadenas
de
caracteres
a
minúsculas
o
a
mayúsculas:
3
import
[Link]
Note
que
7
=
3*2
+
1,
de
donde
3
es
el
cociente
y
1
el
residuo.
En
el
programa
se
utilizó
`div`,
lo
cual
significa
que
se
usa
el
nombre
de
una
función
encerrada
entre
las
comitas
invertidas
“`”
import
[Link]
para
colocar
una
función
en
posición
infija,
es
decir,
en
medio
de
sus
dos
argumentos.
El
ejemplo
anterior
puede
ser
escrito
así:
Una
vez
que
se
ha
escrito
el
programa,
debe
ser
guardado
con
extensión
“.hs”
en
el
mismo
letrasDiferentes
xs
=
length
(nub
(mayusculas
directorio
del
sistema
de
archivos
donde
se
está
ejecutando
el
intérprete
ghci,
por
ejemplo,
[
x
|
x
<-‐
xs,
isLetter
x]))
con
el
nombre
[Link].
Para
cargar
las
definiciones
en
el
modo
interactivo
hay
que
dar
el
comando:
La
función
toLower
convierte
una
letra
en
mayúscula
en
la
respectiva
letra
en
minúscula;
si
el
carácter
no
es
una
letra
en
mayúscula
lo
deja
igual.
Por
otra
parte,
la
función
toUpper
es
>
:load
[Link]
similar
y
convierte
una
letra
en
minúscula
en
una
letra
en
mayúscula.
La
función
isLetter
es
verdadera
si
el
carácter
representa
una
letra.
Estas
tres
funciones
(toLower,
toUpper,
[1
of
1]
Compiling
Main
(
[Link],
interpreted
)
isLetter)
están
definidas
en
la
librería
[Link].
La
función
nub
que
se
encuentra
definida
en
la
librería
[Link]
elimina
los
elementos
repetidos
de
una
lista;
por
lo
tanto,
la
función
Ok,
modules
loaded:
Main.
letrasDiferentes
cuenta
el
número
de
letras
diferentes
que
contiene
la
secuencia
de
caracteres
xs.
6
[ POLITÉCNICO GRANCOLOMBIANO] [ PARADIGMAS DE LA PROGRAMACIÓN ] 7
12.5. Recursión
alineadas
en
la
misma
columna
que
la
palabra
if.
De
no
ser
así,
el
compilador
indicará
que
hay
Contenido
un
error
y
que
este
posiblemente
sea
por
la
indentación.
A
diferencia
de
muchos
lenguajes
de
programación,
Haskell
no
tiene
las
instrucciones
for
ni
while
para
realizar
iteraciones.
No
obstante,
no
se
presenta
ninguna
limitación,
ya
que
se
Es
importante
tener
en
cuenta
que
la
utilización
de
tabuladores
puede
causar
diferentes
utiliza
el
mecanismo
de
recursión
para
realizar
tareas
repetitivas.
errores,
dado
que
los
editores
de
texto
utilizan
distintas
convenciones
para
la
indentación
con
los
mismos,
por
lo
que
se
recomienda
no
utilizar
tabuladores,
sino
únicamente
espacios.
Una
función
es
recursiva
si
al
definirla
aparece
también
en
el
cuerpo
de
la
función.
Es
común
Anterior
haber
trabajado
con
este
tipo
de
definiciones
con
anterioridad,
por
ejemplo,
la
función
Por
la
facilidad
de
lectura
que
se
presenta
en
las
definiciones
de
las
funciones,
se
prefiere
la
factorial
se
precisa
por
estas
dos
ecuaciones:
notación
utilizada
en
la
primera
definición
recursiva
que
la
utilización
de
la
instrucción
condicional.
0!
=
1
Otro
ejemplo
de
función
recursiva
es
calcular
la
suma
de
los
primeros
números
naturales:
n!
=
n*(n-‐1)!
para
n
>
0
Siguiente
sumar
0
=
0
Utilizando
la
misma
definición
se
puede
crear
un
programa
en
Haskell
para
calcular
el
factorial
de
un
número
n:
sumar
n
|
n
>
0
=
n
+
sumar
(n
–
1)
factorial 0 = 1
Note
que
en
la
definición
de
la
función,
en
la
primera
línea,
no
aparece
ningún
llamado
a
la
Aunque
en
Haskell
no
es
necesario
escribir
el
tipo
de
datos
en
los
programas,
se
recomienda
función
factorial
en
la
parte
derecha
del
igual.
Esta
definición
es
llamada
el
caso
base
e
indica
hacerlo
por
claridad
y
documentación
de
las
definiciones
que
se
realizan.
El
tipo
de
datos
se
que
el
factorial
del
número
0
es
1.
anota
mediante
la
utilización
de
::.
Por
ejemplo,
se
puede
anotar
que
la
función
factorial
recibe
un
número
entero
y
retorna
un
número
entero
en
el
programa
presentado
en
la
En
la
segunda
parte
de
la
definición,
se
utiliza
la
función
factorial
en
la
parte
derecha
del
igual,
sección
anterior:
es
decir,
la
función
se
está
llamando
a
sí
misma.
Lo
importante
en
este
llamado
recursivo
es
que
el
valor
con
que
se
llama
(n
–
1)
es
más
pequeño
que
el
argumento
de
la
definición,
en
factorial
::
Integer
-‐>
Integer
este
caso
n.
Adicionalmente,
en
la
segunda
definición,
se
está
colocando
una
restricción
o
condición
en
la
llamada,
esto
es
que
el
valor
de
n
sea
mayor
que
0
(n
>
0).
factorial
0
=
1
Una posibilidad diferente es utilizar una sola definición con el condicional if: factorial n | n > 0 = n * factorial (n – 1)
factorial
n
=
if
n
==
0
Igualmente,
se
pueden
definir
tipos
de
datos
de
diferentes
formas.
La
primera
forma
es
crear
un
alias
o
sobrenombre
a
un
tipo
de
datos
existente.
En
un
caso,
el
tipo
de
datos
String
es
el
then
1
sobrenombre
para
una
lista
de
caracteres
cuyo
tipo
es
[Char].
Los
alias
se
utilizan
en
los
programas
para
dar
mayor
claridad
a
la
lectura
del
texto.
else
n
*
factorial
(n
–
1)
Así,
se
puede
definir
el
tipo
de
datos
Password
como
un
alias
de
String:
El
condicional
if
en
Haskell
siempre
tiene
que
tener
una
parte
then
y
una
parte
else;
acá,
está
preguntando
si
la
variable
n
es
mayor
que
0,
en
cuyo
caso
el
valor
de
la
expresión
condicional
typedef
Password
=
String
es
1;
en
caso
contrario,
el
valor
de
la
expresión
es
n
multiplicado
por
el
factorial
de
(n
-‐
1).
En
Haskell,
la
indentación
o
posición
de
las
instrucciones
en
el
programa
indica
los
bloques
de
instrucciones
que
están
relacionadas.
En
este
caso,
note
que
la
palabra
then
y
else
están
longitudPassword
::
Password
-‐>
Integer
8
[ POLITÉCNICO GRANCOLOMBIANO] [ PARADIGMAS DE LA PROGRAMACIÓN ] 9
10
[ POLITÉCNICO GRANCOLOMBIANO] [ PARADIGMAS DE LA PROGRAMACIÓN ] 11
en
1:(2:(3:[])),
y
como
(:)
asocia
a
derecha,
es
equivalente
a
[Link][].
Utilizando
la
definición
>
map
(\x
-‐>
x^2)
[1..5]
Contenido
que
ya
viene
implementada
en
Haskell,
el
algoritmo
de
la
longitud
sería:
[1,
4,
9,
16,
25]
longitud
[]
=
0
>
map
(\x
-‐>
2*x)
[1..5]
longitud
(x:xs)
=
1
+
longitud
xs
[2,
4,
6,
8,
10]
Anterior
Igualmente,
se
pueden
definir
tipos
de
datos
registro
que
son
equivalentes
al
tipo
de
datos
La
definición
de
una
función
que
recibe
otra
función
como
parámetro
se
presenta
a
producto,
pero
define
un
conjunto
de
funciones
o
proyecciones
para
acceder
a
los
continuación:
elementos.
En
un
caso:
dup
f
x
=
f
(f
x)
data
Persona
=
Persona
{
nombre
:
String,
edad
:
Integer}
>
dup
(\x
-‐>
2*x)
3
Siguiente
Define
el
tipo
de
datos
persona
y
las
funciones
nombre
::
Persona
-‐>
String
y
edad
::
persona
-‐
>
Integer.
12
A
continuación,
se
presentan
algunos
ejemplos
utilizando
este
tipo
de
datos:
>
dup
(\x
-‐>
x^2)
3
>
nombre
(Persona
{nombre
=
”Juan”,
edad
=
21
})
81
Juan
>
nombre
(Persona
”Juan”
21)
Para
tener
en
cuenta
Juan
La
composición
de
funciones
es
un
mecanismo
o
método
de
programación
que
facilita
la
realización
y
el
mantenimiento
de
los
programas
a
partir
de
funciones
o
componentes
para
12.7. Funciones
de
orden
superior
realizar
tareas
más
sencillas
que,
si
se
combinan,
resuelven
problemas
complejos.
Siendo
Haskell
un
lenguaje
funcional,
las
funciones
se
pueden
utilizar
como
parámetros
o
retornar
como
resultados.
En
particular,
esta
capacidad
permite
reutilizar
el
código
de
diferentes
formas.
Por
ejemplo,
map
aplica
una
función
a
los
elementos
de
una
lista,
es
decir
En
resumen
map
f
[x1,x2,
…
,
xn]
=
[f
x1,
f
x2,
…,
f
xn].
Una
función
como
map,
que
recibe
una
función
como
parámetro,
se
denomina
función
de
orden
superior.
• El
lenguaje
de
programación
Haskell
se
puede
utilizar
de
dos
maneras
diferentes:
el
modo
interactivo
para
calcular
expresiones
y
el
modo
compilado
para
generar
A
continuación,
se
presentan
algunos
ejemplos
de
la
utilización
de
la
función
map:
aplicaciones.
• Las
listas
son
una
estructura
de
datos
que
se
utiliza
frecuentemente
en
los
lenguajes
>
map
factorial
[1..5]
de
programación
y
principalmente
en
los
lenguajes
funcionales.
[1,
2,
6,
24,
120]
• En
los
lenguajes
funcionales,
la
definición
de
las
listas
por
lo
general
es
constructiva
y
se
basa
en
dos
construcciones:
la
lista
vacía
y
las
listas
que
tienen
un
elemento
>
map
sumar
[1..5]
denominado
la
cabeza
de
la
lista
y
un
resto
de
elementos
denominado
la
cola
de
la
lista.
[1,
3,
6,
10,
15]
• Las
funciones
recursivas
permiten
realizar
cómputos
repetitivos.
También
se
pueden
utilizar
lambda
abstracciones
en
lugar
de
tener
definidas
las
funciones
en
• Las
funciones
de
orden
superior
permiten
pasar
funciones
como
parámetros,
lo
cual
un
programa:
es
una
manera
conveniente
para
obtener
o
reutilizar
las
funciones.
12
[ POLITÉCNICO GRANCOLOMBIANO] [ PARADIGMAS DE LA PROGRAMACIÓN ] 13