Recursión 2 + Recursión sobre listas Taller de Álgebra I Verano de 2016 Recordando Recursión Ejercicios I potencia :: Float -> Integer -> Float Que calcule la potencia: an con (n ≥ 0). Debe utilizarse el producto (no ^, ∗∗ ni similares). I (Ejercicio 4 clase pasada) sumaImparesCuyoCuadSeaMenorQue :: Integer -> Integer Suma los números impares positivos cuyo cuadrado sea menor que n. sumaImparesCuyoCuadSeaMenorQue 30 1 + 3 + 5 9. s u m a I m p a r e s C u y o C u a d S e a M e n o r Q u e :: Integer -> Integer s u m a I m p a r e s C u y o C u a d S e a M e n o r Q u e umbral = sumaAuxiliar ... ... -- Construimos una funcion que nos facilita el trabajo sumaAuxiliar :: Integer -> Integer -> Integer sumaAuxiliar umbral num | num ^ 2 >= umbral = 0 | otherwise = num + sumaAuxiliar umbral ( num + 2) Algoritmo de división Teorema: Dados a, d ∈ Z, d 6= 0, existen únicos q, r ∈ Z tales que I a = qd + r , I 0 ≤ r < |d|. Idea de la demostración: (caso a ≥ 0, d > 0). Por inducción en a. I Si 0 ≤ a < d, tomamos q = 0, r = a. I Si no, dividimos a − d por d. Eso da un cociente q 0 y un resto r 0 . Tomamos r = r 0 , q = q 0 + 1. Implementar la siguiente función division :: Integer -> Integer -> (Integer, Integer) Debe funcionar para a ≥ 0, d > 0 y no se pueden usar div, mod ni /. Algoritmo de división division :: Integer -> Integer -> ( Integer , Integer ) division a d | a < d = (0 , a ) division a d | otherwise = ( fst ( division (a - d ) d ) + 1 , snd ( division (a - d ) d ) ) ¿Se puede evitar la doble evaluación de division (a-d) d? Sı́: division :: Integer -> Integer -> ( Integer , Integer ) division a d | a < d = (0 , a ) division a d | otherwise = ( fst qr ' + 1 , snd qr ') where qr ' = division (a - d ) d Primos Un entero p > 1 es primo si ningún natural k tal que 1 < k < p divide a p. ¿Se puede hacer una función esPrimo :: Integer -> Bool que diga si un número entero mayor a cero es primo o no? Una posibilidad serı́a buscar todos los divisores. Algo como divisores(n) = {k ∈ Z | 1 ≤ k ≤ n y k|n} Esto se puede hacer en Haskell de forma directa, aunque todavı́a no vimos cómo. Pero podemos definir una lista parcial de divisores: divisoresHasta(n, m) = {k ∈ Z | 1 ≤ k ≤ m y k|n} Implementar las siguientes funciones I divisoresHasta :: Integer -> Integer -> [Integer] Tiene que funcionar bien divisoresHasta n m cuando m ≤ n. I Utilizando divisoresHasta, programar divisores :: Integer -> [Integer] I Utilizando divisores, programar esPrimo :: Integer -> Bool Primos divis oresHas ta :: Integer -> Integer -> [ Integer ] divis oresHas ta n 1 = [1] divis oresHas ta n m | mod n m == 0 = m : div isoresHa sta n (m -1) | otherwise = d ivisore sHasta n (m -1) divisores :: Integer -> [ Integer ] divisores n = d ivisores Hasta n n esPrimo :: Integer -> Bool esPrimo n = length ( divisores n ) == 2 Recursión sobre Listas No todo son números suma :: [ Integer ] -> Integer suma lista | length lista == 0 suma lista | otherwise = 0 = head lista + suma ( tail lista ) Implementemos las siguientes funciones I Implementar y dar el tipo de la productoria de una lista. I Implementar la función reverso :: [a] -> [a] I Mostrar los pasos de reducción para la evaluación de reverso ['a', 'b', 'c'] I Implementar la función capicua :: [Integer] -> Bool que devuelve verdadero si el reverso de una lista de números es la misma lista. Recursión sobre Listas La clase pasada vimos ejemplos de definiciones recursivas. Hoy vamos a continuar con el tema. Recordemos un ejemplo: reverso :: [ Integer ] -> [ Integer ] reverso [] = [] reverso xs = ( reverso ( tail xs ) ) ++ [ head xs ] Truco: Cómo pensar recursivamente con listas I Dada una lista cualquiera: [1,2,3,4,5] I Si ya tengo el resultado recursivo sobre la cola de la lista, es decir el reverso de [2,3,4,5], ¿cómo puedo combinarlo con la cabeza de la lista (1) para obtener el resultado que quiero? Ejercicios (parte 1) 1 Definir suma :: [Integer] -> [Integer] -> [Integer] que dadas dos listas del mismo tamaño devuelva la suma elemento a elemento. Por ejemplo: suma [1, 4, 6] [2, -1, 0] [3, 3, 6]. 2 Definir prodInterno :: [Float] -> [Float] -> Float que calcule el producto interno entre dos listas del mismo tamaño, pensadas como vectores en Rn . Por ejemplo: prodInterno [1, -2, 3, 4] [1, 0, 3, 2] 1*1 + -2*0 + 3*3 + 4*2 18. 3 Implementar noTieneDivisoresHasta :: Integer -> Integer -> Bool noTieneDivisoresHasta m n devuelve True sii ningún número entre 2 y m divide a n. 4 Programar esPrimo :: Integer -> Bool, esta vez usando noTieneDivisoresHasta. Intervalo Ejercicios sobre Listas Implementar las siguientes funciones I pertenece :: Integer -> [Integer] -> Bool que indica si un elemento aparece en la lista. I hayRepetidos :: [Integer] -> Bool que indica si una lista tiene elementos repetidos. I menoresQue :: Integer -> [Integer] -> [Integer] que devuelve la lista pero sólo con los elementos que son menores que el primer parámetro (es decir, filtra aquellos que son mayores o iguales que el número dado). Y dos más I quitarPrimeraAparicionDe :: Integer -> [Integer] -> [Integer] que elimina la primera aparición del elemento en la lista (de haberla). I maximo :: [Integer] -> Integer que, dada una lista no vacı́a, calcula el mayor elemento de la misma. Cambio de base en Haskell I Problema: Dados a, b ∈ Z+ con b > 1, escribir el número a en base b. I Por ejemplo ... 1 2 3 Si a = 10 y b = 2, entonces la respuesta es 1010. Si a = 17 y b = 3, entonces la respuesta es 122. Si a = 145 y b = 10, entonces la respuesta es 145. Implementar en Haskell I La función enBase :: Integer -> Integer -> [Integer] Esta función toma un número y lo transforma a la base pasada como segundo parámetro. Para ello devuelve una lista representando al número. Ejemplo: enBase 3 17 [1,2,2]. I La función deBase :: Integer -> [Integer] -> Integer que toma una base y un número (en forma de la lista de sus dı́gitos) y devuelve el número como Integer. deBase 3 [1,2,2] 17. La conjetura del 196 Los números de Lychrel (http://gaussianos.com/la-conjetura-del-196/) I Tomemos un número natural cualquiera, por ejemplo el 180 I Si damos vuelta sus cifras (081) y lo sumamos al original nos queda 180 + 081 = 261 I Ahora repetimos el proceso con el 261: 261 + 162 = 423 I Lo mismo con el 423: 423 + 324 = 747. ¡Un número capicúa! I ¿Será verdad que para cualquier natural, siempre se llega a un número capicúa? Los llamados números de Lychrel son los números naturales en base 10 que nunca llegan a dar un número capicúa como resultado del proceso. Implementar I La función capicuaEmpezandoCon :: [Integer] -> [Integer] que toma un número (es decir sus dı́gitos en base 10) y realiza el proceso antedicho hasta que se consiga su número capicúa asociado. Ejemplo: capicuaEmpezandoCon [1,8,0] I [7,4,7]. Preparar el “ctrl+c” y ejecutar capicuaPara [1,9,6], ¿qué ocurre? Ejercicios (parte 2) 1 Escribir una función que cambie un número de una base a otra. Debe tomar dos enteros b1 y b2, y un entero n representado en base b1 (expresado como una lista de enteros) y retornar n en base b2 (también expresado como una lista de enteros). 2 Escribir una función que determine si una lista de números enteros está ordenada de manera no-decreciente. 3 Escribir una función que elimine los números repetidos, de haberlos, dejando sólo la última aparición de cada uno. 4 Idem anterior, pero dejando la primera aparición de cada uno. 5 Escribir una función que reciba dos listas ordenadas en forma no-decreciente, y que retorne una lista que contenga la unión de los elementos de las dos listas recibidas, y que también esté ordenada en forma no-decreciente. Por ejemplo, al pasarle [2, 3, 7] y [1, 3, 3, 9], deberı́a devolver [1, 2, 3, 3, 3, 7, 9]. 6 (difı́cil) Escribir una función que dada una lista, la retorne ordenada de menor a mayor.