Introducción a los Algoritmos
Funciones (IAA)

Funciones

Una función es una regla que asigna a cada elemento de un primer conjunto un único elemento de un segundo conjunto.

Diremos que una función le “asigna” a los elementos de un conjunto, llamado dominio, elementos de otro conjunto, llamado codominio. La notación:

f:ABf : A → B

Indica que ff es una función con dominio AA y codominio BB. La definición de una función tiene la forma:

f.xf.x = ⟨expresión que depende de x⟩ donde ff es el nombre de la función y xx es/son la/s variable/s independiente/s.


En las siguientes definiciones identificaremos las variables, las constantes y el nombre de la función.

f.x=5xf.x = 5 ∗ x

• Variable: xx

• Constante: 55

• Nombre de la función: ff


multiplicar.zz.tt=zzttmultiplicar.zz.tt = zz ∗ tt

• Variables: zz,ttzz, tt

• Constantes: Ninguna

• Nombre de la función: multiplicarmultiplicar


duplica.a=a+aduplica.a = a + a

• Variable: aa

• Constante: No hay constantes explícitas en esta expresión

• Nombre de la función: duplicaduplica


Tomando las definiciones del punto anterior evaluemos las siguientes expresiones. Justificaremos cada paso utilizando la notación aprendida.

(multiplicar.(f.5).2)+1(multiplicar.(f.5).2) + 1

{ Definición de f }

(multiplicar.(55).2)+1(multiplicar.(5 * 5).2) + 1

{ Definición de multiplicar }

(55)2+1(5 * 5) * 2 + 1

{ Aritmética }

(252)+1(25 * 2) + 1

{ Aritmética }

50+150 + 1

{ Aritmética }

5151

Tipado de funciones

El tipado de funciones es una forma de especificar el tipo de datos que una función acepta como entrada y el tipo de datos que devuelve como salida.

Tomemos la función gg

g.x.y3xxy>0g.x.y ≐ 3*x − x * y > 0

Esta función toma dos argumentos de tipo NumNum y devuelve un valor de tipo BoolBool. Así, el tipo de gg se declara de la siguiente forma:

g:NumNumBoolg : Num → Num → Bool

Es decir, los tipos de los argumentos se listan primero, siguiendo el orden en el que serán llamados por la función, y en último lugar se coloca el tipo del resultado de evaluar la función.


Ejemplos:

1.

g.y8y g.y ≐ 8 * y

g::Numaag :: Num a ⇒ a

• Toma un argumento de tipo NumNum y devuelve un valor de tipo NumNum.


2.

h.z.wz+wh.z.w ≐ z + w

h::Numaaaah :: Num a ⇒ a → a → a

• Toma dos argumentos de tipo NumNum y devuelve un valor de tipo NumNum.


3.

j.xx0j.x ≐ x ≤ 0

j::NumaaBoolj :: Num a ⇒ a → Bool

• Toma un argumento de tipo NumNum y devuelve un valor de tipo BoolBool.


Definiciones de Funciones por casos

Las definiciones de funciones por casos son una forma de definir funciones que toman diferentes valores dependiendo de los valores de sus argumentos.

Hay ocasiones en las que una sola fórmula no alcanza para definir una función. Así, existen funciones que para una parte del dominio requieren una definición y para otra parte del dominio necesitan de otra definición diferente. Es el caso, por ejemplo, de la función valor absoluto que dado un número devuelve su valor absoluto.

Una definición por casos de una función tendrá la siguiente forma general:

f.x(B0f0  B1f1...  Bnfn)f.x ≐ ( B_0 → f_0 \\ □ \; B_1 → f_1 \\ ... \\ □ \; B_n → f_n )

Donde las BiB_i son expresiones de tipo booleano, llamadas guardas, y las fif_i son expresiones del mismo tipo que el resultado de ff que nos describe algebraicamente la relación para cada caso.

Para un argumento dado el valor de la función se corresponde con la expresión cuya guarda es verdadera para ese argumento.

Al trabajar con expresiones booleanas se hace necesario incorporar a nuestro formalismo los operadores , , ¬¬ que pueden ser utilizados para combinar dos fórmulas para obtener una nueva fórmula. En haskell estos operadores se escriben && , || y not, respectivamente.

Podemos definir distintas funciones para luego, poder implementarlas en Haskell. Por ejemplo:
signo:IntIntsigno : Int → Int, que dado un entero retorna su signo, de la siguiente forma: retorna 11 si xx es positivo, 1-1 si es negativo y 00 en cualquier otro caso.

Podemos definirla como:

signo.x(x>01  x<01  x=00  )signo.x ≐ ( x > 0 → 1 \\ □ \; x < 0 → -1 \\ □ \; x = 0 → 0 \; )

Esta definición, en haskell se anota de la siguiente manera

signo :: Int -> Int
signo x | x > 0 = 1
        | x < 0 = -1
        | x == 0 = 0

Ejercicios resueltos - Definiciones de funciones simples y por casos

1.

entre0y9:IntBoolentre0y9 : Int → Bool, que dado un entero devuelve TrueTrue si el entero se encuentra entre 00 y 99.

entre0y9 :: Int -> Bool
entre0y9 x = x >= 0 && x <= 9 
 
-- entre0y9 5 
-- True

No hace falta aclarar que si el número está dentro del conjunto es TrueTrue y si no FalseFalse ya que Haskell lo entiende por defecto.


2.

rangoPrecio:IntStringrangoPrecio : Int → String, que dado un número que representa el precio de una computadora, retorne “muy barato” si el precio es menor a 20002000, “demasiado caro” si el precio es mayor que 50005000, “hay que verlo bien” si el precio está entre 20002000 y 50005000, y “esto no puede ser!” si el precio es negativo.

rangoPrecio :: Int -> String
rangoPrecio  x | x < 2000 && x > 0 = "muy barato"
               | x > 5000 = "demasiado caro"
               | 2000 <= x && x <= 5000 = "hay que verlo bien"
               | x < 0 = "esto no puede ser!"
 
-- rangoPrecio 3000
-- "hay que verlo bien"

Muy importante aclarar que el precio no puede ser negativo, por lo que se pone la condición x < 0.


3.

absoluto:IntIntabsoluto : Int → Int, que dado un entero retorne su valor absoluto.

absoluto :: Int -> Int
absoluto x | x >= 0 = x
           | x < 0 = -x
 
-- absoluto (-5)
-- 5

En este caso, si el número es positivo, se devuelve el mismo número, y si es negativo, se devuelve el número multiplicado por 1-1.


4.

esMultiplo2:IntBoolesMultiplo2 : Int → Bool, que dado un entero nn devuelve TrueTrue si nn es múltiplo de 22.

esMultiplo2 :: Int -> Bool
esMultiplo2 x = mod x 2 == 0
 
-- esMultiplo2 4
-- True

En este caso, si el número es divisible por 22, se devuelve TrueTrue, y si no, FalseFalse.

Combinado de Funciones

El Combinado de Funciones es una operación que toma dos funciones y las combina para formar una nueva función.

En algunos ejercicios que siguen se van a utilizar algunas de las funciones que están en el Prelude (librería básica de Haskell). Por ejemplo:

modmod 2020 33 = 22 - el resto de la división entre 2020 y 33 es 22.

divdiv 1414 33 = 44 - parte entera de la división entre 1414 y 33 es 44.

maxmax 88 1010 = 1010 - devuelve el max entre 22 números.

minmin 99 1515 = 99 - devuelve el min entre 22 números.

Por ejemplo, si queremos definir la función esMultiploDe:NumNumBoolesMultiploDe : Num→ Num→ Bool , que devuelve TrueTrue si el segundo es múltiplo del primero. Ejemplo: esMultiploDeesMultiploDe 33 112 = TrueTrue.

esMultiploDe :: Int -> Int -> Bool
esMultiploDe x y = (mod y x) == 0
 
-- esMultiploDe 3 12
-- True

En este caso, si el segundo número es divisible por el primero, se devuelve TrueTrue, y si no, FalseFalse.

Ejercicios resueltos - Combinado de Funciones

1.

Definir la función esBisiesto:NumBoolesBisiesto: Num→ Bool, que indica si un año es bisiesto. Un año es bisiesto si es divisible por 400400 o es divisible por 4 pero no es divisible por 100100.

esBisiesto :: Int -> Bool
esBisiesto x = (mod x 400 == 0) || (mod x 4 == 0 && mod x 100 /= 0)
 
-- esBisiesto 2020
-- True

En este caso, si el año es divisible por 400400 o por 44 pero no por 100100, se devuelve TrueTrue, y si no, FalseFalse.


2.

Definir la función dispersion:NumNumNumNumdispersion : Num→ Num→ Num→ Num, que toma los tres valores y devuelve la diferencia entre el más alto y el más bajo. Para este caso, se puede extender max y min a tres argumentos, usando las versiones de dos elementos. De esa forma se puede definir dispersión sin escribir ninguna guarda (las guardas están en max y min, que estamos usando).

dispersion :: Int -> Int -> Int -> Int
dispersion x y z = max x (max y z) - min x (min y z)
 
-- dispersion 3 5 1
-- 4

En este caso, se devuelve la diferencia entre el número más alto y el más bajo.


3.

Definir la función celsiusToFahr:NumNumcelsiusToFahr : Num→ Num, pasa una temperatura en grados Celsius a grados Fahrenheit. Para realizar la conversión podemos multiplicar por 1.81.8 y sumar 3232.

celsiusToFahr :: Float -> Float
celsiusToFahr x = x * 1.8 + 32
 
-- celsiusToFahr 0
-- 32.0

4.

Definir la función fahrToCelsius:NumNumfahrToCelsius : Num→ Num, la inversa de la anterior. Para realizar la conversión hay que primero restar 3232 y después dividir por 1.81.8.

fahrToCelsius :: Float -> Float
fahrToCelsius x = (x - 32) / 1.8
 
-- fahrToCelsius 32
-- 0.0

5.

Definir la función haceFrioF:NumBoolhaceFrioF : Num → Bool, indica si una temperatura expresada en grados Fahrenheit es fría. Decimos que hace frío si la temperatura es menor a 88 grados Celsius.

haceFrioF :: Float -> Bool
haceFrioF x = fahrToCelsius x < 8
 
-- haceFrioF 32
-- False

En este caso, si la temperatura en grados Fahrenheit es menor a 88 grados Celsius, se devuelve TrueTrue, y si no, FalseFalse. Reusamos la función fahrToCelsius que definimos anteriormente.

Tuplas

Una Tupla es una secuencia de elementos de longitud fija. Cada elemento de una tupla puede ser de un tipo diferente.

Una manera de formar un nuevo tipo es combinando los otros ya existentes en tuplas (i.e., haciendo su producto cartesiano). El ejemplo más conocido es, quizás, el de un par de números como (3,2)(3,2). (3,2)(3,2) es un elemento de tipo (Num,Num)(Num, Num).

Por ejemplo, podemos definir una función que toma dos pares y los suma coordenada a coordenada de la siguiente forma:

sumaPares:(Num,Num)(Num,Num)(Num,Num)sumaPares.(a,b).(c,d)  =˙  (a+c,  b+d)\begin{equation*} \begin{aligned} \text{sumaPares} : (\text{Num}, \text{Num}) &\rightarrow (\text{Num}, \text{Num}) \rightarrow (\text{Num}, \text{Num}) \\ \text{sumaPares}.(a, b).(c, d) \; &\dot{=} \; (a+c,\; b+d) \end{aligned} \end{equation*}

Ejercicios resueltos - Tuplas

1.

segundo3:(Num,Num,Num)Numsegundo3 : (Num, Num, Num) → Num, que dada una terna de enteros devuelve su segundo elemento.

segundo3 :: (Int, Int, Int) -> Int
segundo3 (x, y, z) = y
 
-- segundo3 (1, 2, 3)
-- 2

2.

ordena:(Num,Num)(Num,Num)ordena : (Num, Num) → (Num, Num), que dados dos enteros los ordena de menor a mayor.

ordena :: (Int, Int) -> (Int, Int)
ordena (x, y) = (min x y, max x y)
 
-- ordena (3, 1)
-- (1, 3)

En este caso, se devuelve una tupla con el menor número en la primera posición y el mayor en la segunda.


3.

rangoPrecioParametrizado:Num(Num,Num)StringrangoPrecioParametrizado : Num → (Num, Num) → String que dado un número xx, que representa el precio de un producto, y un par (menor, mayor) que represente el rango de precios que uno espera encontrar, retorne “muy barato” si xx está por debajo del rango, “demasiado caro” si está por arriba del rango, “hay que verlo bien” si el precio está en el rango, y “esto no puede ser!” si x es negativo.

rangoPrecioParametrizado :: Int -> (Int, Int) -> String
rangoPrecioParametrizado x (y, z) | x < y && x > 0 = "muy barato"
                                  | x > z = "demasiado caro"
                                  | y <= x && x <= z = "hay que verlo bien"
                                  | x < 0 = "esto no puede ser!"
 
-- rangoPrecioParametrizado 3000 (2000, 5000)
-- "hay que verlo bien"

4.

mayor3:(Num,Num,Num)(Bool,Bool,Bool)mayor3 : (Num, Num, Num) → (Bool , Bool , Bool ), que dada una una terna de enteros devuelve una terna de valores booleanos que indica si cada uno de los enteros es mayor que 33.

mayor3 :: (Int, Int, Int) -> (Bool, Bool, Bool)
mayor3 (x, y, z) = (x > 3, y > 3, z > 3)
 
-- mayor3 (4, 2, 5)
-- (True, False, True)

5.

todosIguales:(Num,Num,Num)BooltodosIguales : (Num, Num, Num) → Bool que dada una terna de enteros devuelva TrueTrue si todos sus elementos son iguales y FalseFalse en caso contrario.

todosIguales :: (Int, Int, Int) -> Bool
todosIguales (x, y, z) = x == y && y == z
 
-- todosIguales (3, 3, 3)
-- True

6.

notaPromedio:(String,Num,Num,Num)NumnotaPromedio : (String, Num, Num, Num) → Num que dada una tupla que representa el nombre de un/a estudiante y las notas que sacó en cada parcial, calcule la nota promedio.

notaPromedio :: (String, Int, Int, Int) -> Int
notaPromedio (nombre, x, y, z) = div (x + y + z) 3
 
-- notaPromedio ("Juan", 7, 8, 9)
-- 8

7.

condicionFinal:(String,Num,Num,Num)StringcondicionFinal : (String, Num, Num, Num) → String que dada una tupla que representa el nombre de un/a estudiante y las notas que sacó en cada parcial, devuelva “promoción” si las 3 notas son mayores a 7, “regular” si no promociona pero las 3 notas son mayores a 4, y “libre” en caso contrario.

condicionFinal :: (String, Int, Int, Int) -> String
condicionFinal (nombre, x, y, z) | x >= 7 && y >= 7 && z >= 7 = "promoción"
                                 | x >= 4 && y >= 4 && z >= 4 = "regular"
                                 |  x <= 3 || y <= 3 || z <= 3 = "libre"
 
-- condicionFinal ("Juan", 7, 8, 9)
-- "promoción"

Listas y sus Operadores

Una Lista es una secuencia de elementos de longitud variable. Cada elemento de una lista puede ser de un tipo diferente.

Ahora, se complejiza el lenguaje de nuestras expresiones agregando listas. Una lista (o secuencia) es una colección ordenada de valores, que deben ser todos del mismo tipo; por ejemplo, [1,2,5][1, 2, 5]. Denotamos a la lista vacía con [][ ].

Contamos con distintos operadores para trabajar con listas, con su notación formal y su equivalente en Haskell.

, llamado pegar o constructor, toma un elemento x (a izquierda) y una lista xs, y devuelve una lista con x como primer elemento y los elementos de xs a continuación. Ej:

El operador es fundamental (se lo denomina constructor), ya que permite construir listas arbitrarias a partir de la lista vacía. toma un elemento xx (a izquierda) y una lista xsxs y devuelve una lista con primer elemento xx seguido de los elementos de xsxs. Por ejemplo:

3    []=[3]1    [2,3]=[1,2,3]3 \; ▹ \; [ ] = [3] \\ 1 \; ▹ \; [2, 3] = [1, 2, 3]

Para denotar listas no vacías utilizamos expresiones de la forma [x,y,...,z][x, y, . . . , z], que son abreviaciones de x    (y    ...(z    [])x \; ▹ \; (y \; ▹ \; . . . (z \; ▹ \; [ ]).

El operador es asociativo a derecha, por lo tanto, es lo mismo escribir x    (y    ...(z    [])x \; ▹ \; (y \; ▹ \; . . . (z \; ▹ \; [ ]) que x    y    ...  z    []x \; ▹ \; y \; ▹ \; . . . \; z \; ▹ \; [ ].

En Haskell se escribe:

3 : [ ] = [3]

#\#, llamado cardinal, toma una lista xxs y devuelve su cantidad de elementos. Ej:

#[1,2,0,5]=4\#[1, 2, 0, 5] = 4

En Haskell se escribe:

length [1, 2, 0, 5] = 4

!! llamado índice, toma una lista xsxs (a izquierda) y un natural nn que indica una posición, y devuelve el elemento de la lista que se encuentra en la posición nn (contando a partir de la posición 0). Ej:

[1,3,3,6,2,3,4,5]  !  4=2.[1, 3, 3, 6, 2, 3, 4, 5] \; ! \; 4 = 2.

Este operador, asocia a izquierda, por lo tanto xs  !  n  !  mxs \; ! \; n \; ! \; m se interpreta como (xs  !  n)  !  m(xs \; ! \; n) \; ! \; m.

En Haskell xs  !  nxs \; ! \; n se escribe xs !! n:

[1, 3, 3, 6, 2, 3, 4, 5] !! 4 = 2

llamado tomar, toma una lista xsxs (a izquierda) y un natural nn que indica una cantidad, y devuelve la sublista con los primeros nn elementos de xsxs. Ej:

[1,2,3,4,5,6]2=[1,2][1, 2, 3, 4, 5, 6] ↑ 2 = [1, 2]

Este operador asocia a izquierda, por lo tanto xsnmxs ↑ n ↑ m se interpreta como (xsn)m(xs ↑ n) ↑ m. En Haskell xsnxs ↑ n se escribe: take n xs:

take 2 [1, 2, 3, 4, 5, 6] = [1, 2]

llamado tirar toma una lista xsxs (a izquierda) y un natural n que indica una cantidad, y devuelve la sublista sin los primeros nn elementos de xsxs. Ej:

[1,2,3,4,5,6]2=[3,4,5,6][1, 2, 3, 4, 5, 6] ↓ 2 = [3, 4, 5, 6]

Este operador se comporta igual al anterior, interpretando xsnmxs ↓ n ↓ m como (xsn)m(xs ↓ n) ↓ m. En Haskell xsnxs ↓ n se escribe: drop n xs:

drop 2 [1, 2, 3, 4, 5, 6] = [3, 4, 5, 6]

llamado concatenar toma una lista xsxs (a izquierda) y otra ysys, y devuelve la lista con todos los elementos de xsxs seguidos de los elementos de ysys. Ej:

[1,2,4]    [1,0,7]=[1,2,4,1,0,7][1, 2, 4] \; ⧺ \; [1, 0, 7] = [1, 2, 4, 1, 0, 7]

Este operador es asociativo por lo que podemos escribir sin ambigüedad expresiones sin paréntesis, como xs    ys  zsxs \; ⧺ \; ys ⧺ \; zs. En Haskell xs    ysxs \; ⧺ \; ys se escribe xs ++ ys.

[1, 2, 4] ++ [1, 0, 7] = [1, 2, 4, 1, 0, 7]

, llamado pegar a izquierda toma una lista xsxs (a izquierda) y un elemento y devuelve una lista con todos los elementos de xsxs seguidos por xx como último elemento. Ej:

[1,2]    3=[1,2,3][1, 2] \; ◃ \; 3 = [1, 2, 3]

Este operador es asociativo a izquierda, luego es lo mismo ([]    z)  ...    y)    x([ ] \; ◃ \; z) \; . . . \; ◃ \; y) \; ◃ \; x que []    z  ...    y    x[ ] \; ◃ \; z \; . . . \; ◃ \; y \; ◃ \; x.

En Haskell xsxxs ◃ x no está definido, pero podríamos utilizar el operador de concatenar para implementarla de la siguiente manera: xs ++ [x]

[1, 2] ++ [3] = [1, 2, 3]

Existen además dos funciones fundamentales sobre listas en Haskell que listamos a continuación.

head, llamada cabeza o head en inglés, toma una lista xs y devuelve su primer elemento. Ej:

head.[1,2,3] = 1.

tail, llamada cola o tail en inglés, toma una lista xs y devuelve la sublista que resulta de eliminar el primer elemento. Ej:

tail.[1,2,3] = [2,3]

Ejercicios resueltos - Listas y sus Operadores

Determinar los resultados en base a su operador:

1.

length [5,6,7]

= { def. de length }

3


2.

[5, 3, 57] !! 1

= { def. de !! }

3


3.

[0, 11, 2, 5] : [ ]

= { def. de : }

[0, 11, 2, 5]


4.

take 2 [5, 6, 7]

= { def. de take }

[5, 6]


5.

drop 2 [5, 6, 7]

= { def. de drop }

[7]


6.

head (0 : [1, 2, 3])

= { def. de : }

head [0, 1, 2, 3]

= { def. de head }

0


7.

([1, 2] ++ [3, 4]) ++ [2 + 3]

= { def. de ++ }

[1, 2, 3, 4] ++ [2 + 3]

= { def. de ++ }

[1, 2, 3, 4, 5]

Funciones recursivas

Una función recursiva es una función que se llama a sí misma dentro de su definición.

Una función recursiva es una función tal que en su definición puede aparecer su propio nombre. Sin embargo, ¿Cómo lograr que no sea una definición circular? La clave está en el principio de inducción: en primer lugar hay que definir la función para el (los) caso(s) más “pequeño(s)”, que llamaremos caso base y luego definir el caso general en términos de algo más “chico”, que llamaremos caso inductivo.

El caso base no debe aparecer el nombre de la función que se está definiendo. El caso inductivo es donde aparece el nombre de la función que se está definiendo, y debe garantizarse que el (los) argumento(s) al cual se aplica en la definición es más “chico” (para alguna definición de más chico) que el valor para la que se está definiendo.

Para dar un ejemplo de función recursiva, supongamos que tenemos una lista de números y queremos sumarlos. Podemos definir la función sumatoriasumatoria de la siguiente manera:

sumatoria:[Int]Intsumatoria.[]  =˙  0sumatoria.(x    xs)  =˙  x+sumatoria.xs\begin{equation*} \begin{aligned} &sumatoria : [Int] \rightarrow Int \\ &sumatoria.[ ] \; \dot{=} \; 0 \\ &sumatoria.(x \;▹ \;xs) \; \dot{=} \; x + sumatoria.xs \end{aligned} \end{equation*}

En Haskell, la función sumatoriasumatoria se escribe de la siguiente manera:

sumatoria :: [Int] -> Int
 
sumatoria [ ] = 0
sumatoria (x:xs) = x + sumatoria xs
 
-- Si corremos la función sumatoria [1, 2, 3] obtendremos 6:
 
-- sumatoria [1, 2, 3] = sumatoria (1 : 2 : 3 : [ ]) 
-- = 1 + sumatoria (2 : 3 : [ ])
-- = 1 + 2 + sumatoria (3 : [ ])
-- = 1 + 2 + 3 + sumatoria [ ]
-- = 1 + 2 + 3 + 0 = 6

Existen algunos tipos de funciones recursivas como:

  • Filter: toma una lista y devuelve una lista con los elementos que cumplen una condición.

  • Map: toma una lista y devuelve una lista con los elementos transformados por una función.

  • Fold: toma una lista y devuelve un valor resultante de combinar los elementos de la lista.

  • Zip: aquella que dadas dos listas devuelve una lista de pares donde el primer elemento de cada par se corresponde con la primera lista, y el segundo elemento de cada par se corresponde con la segunda lista.

  • Unzip: aquella que dada una lista de tuplas devuelve una lista de alguna de las proyecciones de la tupla.

Ejercicios resueltos - Funciones recursivas

Definir recursivamente las siguientes funciones de tipo filter.

1.

soloPares:[Int][Int]soloPares : [Int] → [Int], que dada una lista de enteros xsxs devuelve una lista sólo con los números pares contenidos en xsxs, en el mismo orden y con las mismas repeticiones (si las hubiera).

Por ejemplo: soloPares.[3,0,2,12]=[0,2,12]soloPares.[3,0,-2,12] = [0,-2, 12]

soloPares :: [Int] -> [Int]
soloPares [] = []
soloPares (x:xs) | mod x 2 == 0 = x : soloPares xs
                 | mod x 2 /= 0 = soloPares xs
 
-- soloPares [3, 0, -2, 12]
-- [0, -2, 12]

2.

mayoresQue10:[Int][Int]mayoresQue10 : [Int] → [Int], que dada una lista de enteros xsxs devuelve una lista sólo con los números mayores que 1010 contenidos en xsxs.

Por ejemplo: mayoresQue10.[3,0,2,12]=[12]mayoresQue10.[3,0,-2, 12] = [12]

mayoresQue10 :: [Int] -> [Int]
mayoresQue10 [] = []
mayoresQue10 (x:xs) | x > 10 = x : mayoresQue10 xs
                    | x <= 10 = mayoresQue10 xs
 
-- mayoresQue10 [3, 0, -2, 12]
-- [12]

3.

mayoresQue:Int[Int][Int]mayoresQue : Int → [Int] → [Int], que dado un entero nn y una lista de enteros xsxs devuelve una lista sólo con los números mayores que nn contenidos en xsxs.

Por ejemplo: mayoresQue2.[3,0,2,12]=[3,12]mayoresQue2.[3,0,-2, 12] = [3,12]

mayoresQue :: Int -> [Int] -> [Int]
mayoresQue n [] = []
mayoresQue n (x:xs) | x > n = x : mayoresQue n xs
                    | x <= n = mayoresQue n xs
 
-- mayoresQue 2 [3, 0, -2, 12]
-- [3, 12]

Definir recursivamente las siguientes funciones de tipo map.

4.

sumar1:[Int][Int]sumar1: [Int] → [Int], que dada una lista de enteros le suma 11 a cada uno de sus elementos.

Por ejemplo: sumar1.[3,0,2]=[4,1,1]sumar1.[3, 0, -2] = [4, 1, -1]

sumar1 :: [Int] -> [Int]
sumar1 [] = []
sumar1 (x:xs) = (x + 1) : sumar1 xs
 
-- sumar1 [3, 0, -2]
-- [4, 1, -1]

5.

duplica:[Int][Int]duplica: [Int] → [Int], que dada una lista de enteros duplica cada uno de sus elementos.

Por ejemplo: duplica.[3,0,2]=[6,0,4]duplica.[3, 0, -2] = [6, 0, -4]

duplica :: [Int] -> [Int]
duplica [] = []
duplica (x:xs) = (x * 2) : duplica xs
 
-- duplica [3, 0, -2]
-- [6, 0, -4]

En este caso, se duplica cada número de la lista.


6.

multiplica:Int[Int][Int]multiplica: Int → [Int] → [Int], que dado un número nn y una lista, multiplica cada uno de los elementos por nn.

Por ejemplo: multiplica.3.[3,0,2]=[9,0,6]multiplica.3.[3, 0, −2] = [9, 0, −6]

multiplica :: Int -> [Int] -> [Int]
multiplica n [] = []
multiplica n (x:xs) = (x * n) : multiplica n xs
 
-- multiplica 3 [3, 0, -2]
-- [9, 0, -6]

Definir recursivamente las siguientes funciones de tipo fold.

7.

todosMenores10:[Int]BooltodosMenores10 : [Int] → Bool, que dada una lista devuelve TrueTrue si ésta consiste sólo de números menores que 1010.

Por ejemplo: todosMenores10.[1,3,9]=TruetodosMenores10.[1,3,9] = True

todosMenores10 :: [Int] -> Bool
todosMenores10 [] = True
todosMenores10 (x:xs) | x < 10 = todosMenores10 xs
                      | x >= 10 = False
 
-- todosMenores10 [1, 3, 9]
-- True

8.

hay0:[Int]Boolhay0 : [Int] → Bool, que dada una lista decide si existe algún 00 en ella.

Por ejemplo: hay0.[1,0,3]=Truehay0.[1,0,3] = True

hay0 :: [Int] -> Bool
hay0 [] = False
hay0 (x:xs) | x == 0 = True
            | x /= 0 = hay0 xs
 
-- hay0 [1, 0, 3]
-- True

9.

suma:[Int]Intsuma : [Int] → Int, que dada una lista devuelve la suma de todos sus elementos.

Por ejemplo: suma.[1,2,3]=6suma.[1,2,3] = 6

suma :: [Int] -> Int
suma [] = 0
suma (x:xs) = x + suma xs
 
-- suma [1, 2, 3]
-- 6

En este caso, se devuelve la suma de todos los números de la lista.


Definir recursivamente la siguiente función de tipo zip.

10. repartir:[String][String][(String,String)]repartir : [String] → [String] → [(String,String)], que dadas dos listas de strings devuelve una lista de pares donde el primer elemento de cada par se corresponde con la primera lista, y el segundo elemento de cada par se corresponde con la segunda lista.

repartir :: [String] -> [String] -> [(String, String)]
repartir [] [] = []
repartir (x:xs) (y:ys) = (x, y) : repartir xs ys
 
-- repartir ["Juan", "Maria"] ["Dominguez", "Gutierrez"]
-- [("Juan", "Dominguez"), ("Maria", "Gutierrez")]

Definir recursivamente la siguiente función de tipo unzip.

apellidos:[(String,String,Int)][String]apellidos : [(String, String, Int)] → [String].

Ej: apellidos.[("Juan","Dominguez",22),("Maria","Gutierrez",19)] ["Dominguez","Gutierrez"]apellidos.[("Juan","Dominguez",22), ("Maria","Gutierrez",19)]\ ["Dominguez","Gutierrez"]

apellidos :: [(String, String, Int)] -> [String]
apellidos [] = []
apellidos ((x, y, z):xs) = y : apellidos xs
 
-- apellidos [("Juan", "Dominguez", 22), ("Maria", "Gutierrez", 19)]
-- ["Dominguez", "Gutierrez"]