Saltar a contenido

Práctica 10: Programación funcional en Swift (2)

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 con el compilador de Swift todos los ejemplos de los siguientes apartados del tema 5 Programación funcional con Swift

  • Opcionales
  • Clausuras
  • Funciones de orden superior
  • Genéricos

Ejercicios

Ejercicio 1

a) Define la función maxOpt(_ x: Int?, _ y: Int?) -> Int? que devuelve el máximo de dos enteros opcionales. En el caso en que ambos sean nil se devolverá nil. En el caso en que uno sea nil y el otro no se devolverá el entero que no es nil. En el caso en que ningún parámetro sea nil se devolverá el mayor.

Ejemplo:

let res1 = maxOpt(nil, nil) 
let res2 = maxOpt(10, nil)
let res3 = maxOpt(-10, 30)
print("res1 = \(String(describing: res1))")
print("res2 = \(String(describing: res2))")
print("res3 = \(String(describing: res3))")
// Imprime:
// res1 = nil
// res2 = Optional(10)
// res3 = Optional(30)

b1) Escribe una nueva versión del ejercicio 2b) de la práctica 9 que permita recibir números negativos y que devuelva una pareja de (Int?, Int?) con nil en la parte izquierda y/o derecha si no hay número impares o pares. Debes usar la función auxiliar definida en el apartado anterior.

Ejemplo:

let numeros = [-10, 202, 12, 100, 204, 2]
print("\n******\n1b1) Función parejaMayorParImpar2(numeros:)\n******")
print(parejaMayorParImpar2(numeros: numeros))
// Imprime:
// parejaMayorParImpar2(numeros: [-10, 202, 12, 100, 204, 2])
// (nil, Optional(204))

b2) Escribe la función sumaMaxParesImpares(numeros: [Int]) -> Int que llama a la función anterior y devuelve la suma del máximo de los pares y el máximo de los impares. En el caso en que se pase un array vacío deberá devolver un 0.

print("sumaMaxParesImpares(numeros: \(numeros))")
print(sumaMaxParesImpares(numeros: numeros))
// Imprime:
// sumaMaxParesImpares(numeros: [-10, 202, 12, 100, 204, 2])
// 204

b3) Escribe una nueva versión de la función del ejercicio b1) en la que se devuelva nil en el caso en que se le pase como parámetro un array vacío. ¿Cómo se debería cambiar la declaración de la función? Escribe también una nueva versión de la función del ejercicio b2) en la que se llame a la función anterior.

Ejercicio 2

a) Indica qué devuelven las siguientes expresiones:

a.1)

let nums = [1,2,3,4,5,6,7,8,9,10]
nums.filter{$0 % 3 == 0}.count

a.2)

let nums2 = [1,2,3,4,5,6,7,8,9,10]
nums2.map{$0+100}.filter{$0 % 5 == 0}.reduce(0,+)

a.3)

let cadenas = ["En", "un", "lugar", "de", "La", "Mancha"]
cadenas.sorted{$0.count < $1.count}.map{$0.count}

a.4)

let cadenas2 = ["En", "un", "lugar", "de", "La", "Mancha"]
cadenas2.reduce([]) {
    (res: [(String, Int)], c: String) -> [(String, Int)] in
        res + [(c, c.count)]}.sorted(by: {$0.1 < $1.1})

b) Explica qué hacen las siguientes funciones y pon un ejemplo de su funcionamiento:

b.1)

func f(nums: [Int], n: Int) -> Int {
    return nums.filter{$0 == n}.count
}

b.2)

func g(nums: [Int]) -> [Int] {
    return nums.reduce([], {
        (res: [Int], n: Int) -> [Int] in
            if !res.contains(n) {
                return res + [n]
            } else {
                return res
            }
    })
}

b.3)

func h(nums: [Int], n: Int) -> ([Int], [Int]) {
   return nums.reduce(([],[]), {
       (res: ([Int],[Int]), num: Int ) -> ([Int],[Int]) in
           if (num >= n) {
               return (res.0, res.1 + [num])
           } else {
               return ((res.0 + [num], res.1))
           }
   })
}

c) Implementa las siguientes funciones con funciones de orden superior.

c.1) Función suma(palabras:contienen:):

suma(palabras: [String], contienen: Character) -> Int

que recibe una array de cadenas y devuelve la suma de las longitudes de las cadenas que contiene el carácter que se pasa como parámetro.

c.2) Función sumaMenoresMayores(nums:pivote:):

sumaMenoresMayores(nums: [Int], pivote: Int) -> (Int, Int)

que recibe un array de números y un número pivote y devuelve una tupla con la suma de los números menores y mayores o iguales que el pivote.

d) (Ejercicio sobre variables capturadas por clausuras) Reflexiona sobre el siguiente código y completa el hueco para obtener el resultado esperado.

func bar(f: (Int) -> Int) {
  print(f(__________))
}

func foo() -> (Int) -> Int {
  var x = 3
  return {
    x += $0 + 2
    return x
  }
}

var x = 5
let g = foo()
bar(f: g)   // => 9
bar(f: g)   // => 15

Ejercicio 3

Define un tipo enumerado con un árbol genérico, tal y como hicimos en el último ejercicio de la práctica anterior, que tenga como genérico el tipo de dato que contiene.

En el siguiente ejemplo vemos cómo debería poderse definir con el mismo tipo genérico un árbol de enteros y un árbol de cadenas:

let arbolInt: Arbol = .nodo(53, 
                            [.nodo(13, []), 
                             .nodo(32, []), 
                             .nodo(41, 
                                   [.nodo(36, []), 
                                    .nodo(39, [])
                                   ])
                            ])
let arbolString: Arbol = .nodo("Zamora", 
                               [.nodo("Buendía", 
                                      [.nodo("Albeza", []), 
                                       .nodo("Berenguer", []), 
                                       .nodo("Bolardo", [])
                                      ]), 
                                .nodo("Galván", [])
                               ])

Define las funciones genéricas toArray y toArrayFOS que devuelvan un array con todos los componentes del árbol usando un recorrido preorden (primero la raíz y después los hijos). La primera la debes implementar con recursión mutua y la segunda usando funciones de orden superior.

Ejemplo:

print(toArray(arbol: arbolInt))
// Imprime: [53, 13, 32, 41, 36, 39]
print(toArrayFOS(arbol: arbolString))
// Imprime: ["Zamora", "Buendía", "Albeza", "Berenguer", "Bolardo", "Galván"]

Ejercicio 4

Implementa en Swift la función imprimirListadosNotas(alumnos:) que recibe un array de tuplas, en donde cada tupla contiene información de la evaluación de un alumno de LPP (nombreAlumno, notaParcial1, notaParcial2, notaParcial3, añosMatriculacion) y que debe imprimir por pantalla los siguientes listados:

  • listado 1: array ordenado por nombre del alumno (orden alfabético creciente)
  • listado 2: array ordenado por la nota del parcial 1 (orden decreciente de nota)
  • listado 3: array ordenado por la nota del parcial 2 (orden creciente de nota)
  • listado 4: array ordenado por año de matriculación y nota del parcial 3 (orden decreciente de año y nota)
  • listado 5: array ordenado por nota final (media de los tres parciales, ponderados en: 0,35, 0,3, 0,35) (orden decreciente de nota final)

Las ordenaciones hay que realizarlas usando la función sorted.

Nota

Para que los listados se muestren formateados con espacios, puedes usar la siguiente función (para ello también debes incluir el import que se indica)

import Foundation

func imprimirListadoAlumnos(_ alumnos: [(String, Double, Double, Double, Int)]) {
    print("Alumno   Parcial1   Parcial2   Parcial3  Años")
    for alu in alumnos {
        alu.0.withCString {
            print(String(format:"%-10s %5.2f      %5.2f    %5.2f  %3d", $0, alu.1,alu.2,alu.3,alu.4))
        }
    }
}

Ejemplo:

let listaAlumnos = [("Pepe", 8.45, 3.75, 6.05, 1), 
                    ("Maria", 9.1, 7.5, 8.18, 1), 
                    ("Jose", 8.0, 6.65, 7.96, 1),
                    ("Carmen", 6.25, 1.2, 5.41, 2), 
                    ("Felipe", 5.65, 0.25, 3.16, 3), 
                    ("Carla", 6.25, 1.25, 4.23, 2), 
                    ("Luis", 6.75, 0.25, 4.63, 2), 
                    ("Loli", 3.0, 1.25, 2.19, 3)]
imprimirListadosNotas(listaAlumnos)

Algunos de los listados que se deben mostrar serían los siguientes:

LISTADO ORIGINAL
Alumno   Parcial1   Parcial2   Parcial3  Años
Pepe        8.45       3.75     6.05    1
Maria       9.10       7.50     8.18    1
Jose        8.00       6.65     7.96    1
Carmen      6.25       1.20     5.41    2
Felipe      5.65       0.25     3.16    3
Carla       6.25       1.25     4.23    2
Luis        6.75       0.25     4.63    2
Loli        3.00       1.25     2.19    3

LISTADO ORDENADO por Parcial1 (decreciente)
Alumno   Parcial1   Parcial2   Parcial3  Años
Loli        3.00       1.25     2.19    3
Felipe      5.65       0.25     3.16    3
Carmen      6.25       1.20     5.41    2
Carla       6.25       1.25     4.23    2
Luis        6.75       0.25     4.63    2
Jose        8.00       6.65     7.96    1
Pepe        8.45       3.75     6.05    1
Maria       9.10       7.50     8.18    1

Ejercicio 5

Dado el array listaAlumnos del ejercicio anterior, utiliza funciones de orden superior para obtener los datos requeridos en cada caso.

A) Número de alumnos que han aprobado primer parcial y suspendido el segundo

print(listaAlumnos. ________________________________ )
// Resultado: 5

B) Alumnos que han aprobado la asignatura (tienen una nota final >= 5)

print(listaAlumnos._______________________________ )

// Resultado: ["Pepe", "Maria", "Jose"]

C) Nota media de todos los alumnos en forma de tupla (media_p1, media_p2, media_p3)

var tupla = listaAlumnos._____________________________________ )
tupla = (tupla.0 / Double(listaAlumnos.count), tupla.1 / Double(listaAlumnos.count), tupla.2 / Double(listaAlumnos.count))
print(tupla)
// Resultado: (6.6812499999999995, 2.7624999999999997, 5.22625)

Ejercicio 6

Implementa la función construye con el siguiente tipo:

func construye(operador: Character) -> (Int, Int) -> Int

La función recibe un operador que puede ser uno de los siguientes caracteres: +, -, *, / y debe devolver una clausura que reciba dos argumentos y realice la operación indicada sobre ellos.

Ejemplo:

var f = construye(operador: "+")
print(f(2,3))
// Imprime 5
f = construye(operador: "-")
print(f(2,3))
// Imprime -1

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