Saltar a contenido

Práctica 6: Procedimientos recursivos e iterativos

Antes de la clase de prácticas

  • Los siguientes ejercicios están basados en los conceptos de teoría vistos la semana pasada. Antes de la clase de prácticas debes repasar todos los conceptos y probar en el DrRacket todos los ejemplos de los siguientes apartados del tema 3 Procedimientos recursivos:

    • 1 El coste de la recursión
    • 2 Procesos iterativos
    • 3 Memoization
    • 4 Figuras recursivas

Ejercicios

Descarga el fichero lpp.rkt, pulsando el botón derecho del ratón y seleccionando la opción Guardar como lpp.rkt. Guárdalo en la misma carpeta en la que tengas el fichero practica6.rkt.

El fichero contiene la definición de las funciones (crea-diccionario), (put key value dic) y (get key dic), necesarias para realizar una implementación de un algoritmo recursivo con memoization y que necesitarás en el ejercicio 4.

Ejercicio 1

a) Implementa una versión recursiva iterativa de la función (concat lista) que toma como argumento una lista de cadenas y devuelve una cadena resultante de concatenar todas las palabras de la lista.

La función concat deberá llamar a la función concat-iter que es la que implementa propiamente la versión iterativa usando recursión por la cola.

Ejemplo:

(concat  '("hola" "y" "adiós")) ; ⇒ "holayadiós"
(concat-iter '("hola" "y" "adiós") "") ; ⇒ "holayadiós"

b) Define utilizando recursión por la cola la función (min-max lista) que recibe una lista numérica y devuelve una pareja con el mínimo y el máximo de sus elementos. La lista recibida como parámetro tendrá como mínimo un elemento.

Ejemplo:

(min-max '(2 5 9 12 5 0 4)) ; ⇒ (0 . 12)
(min-max '(3 2 -8 4 10 0)) ; ⇒ (-8 . 10)
(min-max-iter '(5 9 12 -2 5 0 4) (cons 2 2)) ; ⇒ (-2 . 12)

Ejercicio 2

a) Implementa utilizando recursión por la cola las funciones expande-pareja y expande-parejas de la práctica 4.

Ejemplo:

(expande-pareja (cons 'a 4)) ; ⇒ (a a a a)
(expande-parejas '(#t . 3) '("LPP" . 2) '(b . 4))
; ⇒ (#t #t #t "LPP" "LPP" b b b b)

b) Implementa utilizando recursión por la cola la función (rotar k lista) que mueve k elementos de la cabeza de la lista al final. No es necesario utilizar una función iterativa auxiliar, puedes hacer que la propia función rotar sea iterativa usando el parámetro lista como el parámetro donde acumular el resultado.

Ejemplo:

(rotar 4 '(a b c d e f g)) ; ⇒ (e f g a b c d)

Ejercicio 3

a) Implementa utilizando recursión por la cola la función mi-foldl que haga lo mismo que la función de orden superior foldl.

(mi-foldl string-append "****" '("hola" "que" "tal"))  "talquehola****"
(mi-foldl cons '() '(1 2 3 4)) ; ⇒ (4 3 2 1)

b) Existe un algoritmo eficiente para calcular el valor decimal de un número binario basado en usar de forma iterativa una multiplicación por 2. La idea es que si a un número binario le añadimos un dígito a su derecha, el valor del número resultante es el valor del número original multiplicado por 2 más el dígito que hemos añadido.

Por ejemplo, si tenemos el número 101, que es el número decimal 5, y le añadimos un 1 a su derecha (obteniendo el 1011) el número decimal resultante se obtendría multiplicando por 2 el número original (5*2 = 10) y sumándole el 1 que hemos añadido (11).

De esta forma, podemos calcular de forma iterativa el valor decimal de un número binario haciendo esta operación con sus dígitos de izquierda a derecha. Deberíamos ir acumulando en un resultado el valor del número procesado y, en cada nuevo paso, multiplicar por 2 ese valor y sumar el valor del nuevo dígito que estamos procesando.

resultado nuevo = resultado anterior * 2 + nuevo bit

Supongamos el número binario anterior, el 1011. Veamos una traza de cómo se obtendría el 11.

 número       nuevo        resultado    resultado
 procesado    bit          anterior      nuevo 
=======================================================
                 1            0         0*2 + 1 = 1
    1            0            1         1*2 + 0 = 2
    10           1            2         2*2 + 1 = 5
    101          1            5         5*2 + 1 = 11
    1011                      11

Implementa, usando el algoritmo anterior iterativo, la función (binario-a-decimal lista-bits) que reciba una lista de bits que representan un número en binario (el primer elemento será el bit más significativo) y devuelva el número decimal equivalente.

(binario-a-decimal '(1 1 1 1)) ; ⇒ 15
(binario-a-decimal '(1 1 0)) ; ⇒ 6
(binario-a-decimal '(1 0)) ; ⇒ 2

Ejercicio 4

Realiza una implementación que utilice la técnica de la memoization del algoritmo que devuelve la serie de Pascal.

(define diccionario (crea-diccionario))
(pascal-memo 8 4 diccionario) ; ⇒ 70
(pascal-memo 40 20 diccionario) ; ⇒ 137846528820

Ejercicio 5

a) Usando la librería de imágenes de Racket 2htdp/image implementa la figura recursiva conocida como curva de Koch. Debes definir una función recursiva (koch nivel trazo) que dibuje una curva de Koch de nivel nivel y de longitud trazo.

Como pista, fíjate en el dibujo. Para construir una imagen de una curva de Koch de nivel n y longitud l, se deberán juntar 4 curvas de Koch de nivel n-1 y longitud l/3. La primera y la última imagen son la curva original y la segunda y tercera están rotadas 60 grados. Fíjate también en la alineación de las imágenes.

Puedes ver ejemplos de las curvas de nivel 1, 2 y 3 en las siguientes figuras:

b) Usando la función anterior, implementa la función (copo-nieve nivel trazo) que dibuje el copo de nieve de Koch que puedes ver en los siguientes ejemplos. Esta función no es recursiva, se construye combinando tres veces la curva de Koch anterior.

Ejercicio 6

Define la función (alfombra-sierpinski tam) que construya la alfombra de Sierpinski (una variante del triángulo de Sierpinski que hemos visto en teoría) de lado tam píxeles.

En el caso base, cuando el tamaño sea menor que un umbral determinado, se debe dibujar un círculo sin relleno de ancho tam. Fíjate que el parámetro que se le pasa a la primitiva circle es el del radio (lo puedes consultar aquí), por lo que para dibujar un círculo de ancho (diámetro) tam habrá que llamar a la primitiva con el parámetro tam/2.

Por ejemplo, la llamada a (alfombra-sierpinski 360), poniendo como umbral 20 píxeles, debe dibujar la siguiente figura:


Lenguajes y Paradigmas de Programación, curso 2023-24
© Departamento Ciencia de la Computación e Inteligencia Artificial, Universidad de Alicante
Domingo Gallardo, Cristina Pomares, Antonio Botía, Francisco Martínez