• ¿Qué es un lenguaje de programación?
  • Entorno de Desarrollo en macOS
WeiseRatel
  • ¿Qué es Swift?
  • ¿Hablamos?
  • Sobre WeiseRatel.com
No Result
View All Result
  • ¿Qué es Swift?
  • ¿Hablamos?
  • Sobre WeiseRatel.com
No Result
View All Result
WeiseRatel
No Result
View All Result
Home Lenguajes de Programación Swift

Swift – Funciones de Órden Superior

WeiseRatel by WeiseRatel
in Swift
Reading Time: 14 mins read
A A
Funciones de Órden Superior, Swift
11
SHARES
36
VIEWS
Compartir en FacebookCompartir en Twitter

En este encuentro aprenderemos sobre las funciones de orden superior y como estas nos pueden ayudan en nuestro día a día. Un tema clave en la programación de hoy día ya que son de uso frecuente en la programación funcional.

[better-ads type=’banner’ banner=’159′ ]

¿Qué es una función de órden superior?

En programación funcional a las funciones se les clasifica como ciudadanos de primera clase (first-class citizen). Esto se debe a que son tratadas como cualquier otro tipo.

Pueden ser asignadas a variables, pueden también aceptar otras funciones como parámetro y a su vez retornar otras funciones. Las funciones que describen estas características son llamadas también: funciones de orden superior.

Si esto último te suena al artículo sobre funciones, a las funciones como tipos o a closures pues tienes razón. Lo único a señalar es que en este artículo nos enfocaremos en lo que corresponde al ámbito de la programación funcional.

Veamos a continuación las funciones de uso más frecuente.

Map

Esta función itera sobre cada elemento de una colección y aplica una misma operación sobre cada una, pero antes veamos como lograr esto de la manera «clásica»:

var numberArray = [ 1, 2, 3, 4, 5 ]

var emptyArray: [Int] = []

for number in numberArray {

    emptyArray.append(number * 2)

} // for

print(emptyArray)

En este ejemplo tenemos un arreglo de cinco enteros de nombre numberArray al igual que otro vacío llamado emptyArray y donde almacenaremos el resultado de multiplicar cada elemento del primer arreglo por 2.

A favor de lograr esto creamos un bucle for en el cual iteramos sobre el arreglo numberArray, donde haciendo uso del método append agregamos a emptyArray el resultado de la multiplicación.

Ahora veamos como lograr lo mismo haciendo uso de map:

var numberArray = [ 1, 2, 3, 4, 5 ]

var newArray = numberArray.map () {

    value in value * 2

}

print(newArray)

En este ejemplo vemos como map forma parte de Array y recibe un closure donde se nos permite a los clientes de esta función definir las operaciones a realizar sobre cada uno de los elementos que tiene mapeado.

Dentro del closure hacemos referencia al valor del arreglo y lo multiplicamos por 2. La salida en pantalla es idéntica a la anterior:

[2, 4, 6, 8, 10]

Creo que estarán de acuerdo conmigo con que esta versión es mucho más sencilla y limpia que la anterior.

Filter

La función filter itera sobre una colección y retorna un arreglo que contiene elementos que coinciden con cierta condición y de ahí su nombre filtro.

Asumamos ahora que necesitamos filtrar los números pares de un arreglo. Usualmente resolveríamos este problema de la siguiente forma:

let numberArray = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15 ]

var evenNumArray: [Int] = []

for number in  numberArray {
    
    if (number % 2) == 0 {
        
        evenNumArray.append(number)
        
    } // if
    
} // for

print(evenNumArray)

mientras que con filter podemos simplificar el código a este punto:

let numberArray = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 14, 15 ]

let evenArray = numberArray.filter { $0 % 2 == 0 }

print(evenArray)

la salida en pantalla es:

[2, 4, 6, 8, 10, 14]

Nuevamente vemos como haciendo uso de las funciones de orden superior podemos lograr alternativas muchos más legibles que las clásicas.

Reduce

Como las otras dos funciones reduce toma una secuencia como entrada mientras que el producto final es más genérico ya que este puede ser de cualquier tipo.

Con esta función podemos reducir todos los elementos de una colección en un solo valor. ¿Cómo así? Pues sí amigos, por cada iteración del bucle (me refiero al bucle implementado en reduce) reduce pasa el producto de la anterior ciclo en conjunto al elemento actual de la secuencia.

[better-ads type=’banner’ banner=’160′ ]

Pero vamos por partes, imaginemos que necesitamos obtener la suma de todos los números miembros de un arreglo de enteros. Una solución sería implementar otro bucle for:

let integerArray = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

var total: Int = 0

for number in integerArray {

    total += number
    
} // for

print(total)

Pero como ya deben de suponer con reduce podemos lograr algo mucho más cool y óptimo:

let integerArray = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

let total = integerArray.reduce(0, { $0 + $1 } )

print(total)

obteniendo la misma salida en pantalla:

55

La función reduce toma dos parámetros, en el primero initialResult es donde especificamos el valor inicial acumulativo, es decir este campo sería el homologo a la variable total del primer ejemplo.

El segundo parámetro es un closure de nombre next​Partial​Result el cual combina un valor acumulativo (initialResult) junto con un valor de la secuencia con el objetivo de producir un nuevo valor acumulativo.

Este nuevo valor acumulativo será usado a su vez en la próxima llamada y así hasta que se llegue al último elemento de la colección y por último sea retornado como un único valor final, en este ejemplo almacenado en la constante total.

Resumiendo, la función reduce pasará en cada ciclo el resultado parcial acumulado junto al próximo elemento de la secuencia, ambos como argumentos del closure. Tal y como podemos constatar en la siguiente imagen:

Swift - Funciones de Órden Superior - WeiseRatel
Diagrama de la función reduce

Digamos que tenemos ahora el código:

let primes = [3, 7, 31, 127]

let sum = primes.reduce(0) { (result, item) in
    
    return result + item

}

print(sum)

En el primer ciclo de iteración sobre este arreglo de número primos se llamará al closure con los parámetros (0, 3) donde «evidentemente» 0 es el valor inicial y 3 es el primer elemento de la secuencia.

La suma entonces será 0 + 3 y por ende el valor 3 será almacenado en pos de ser pasado como result. En efecto, en el siguiente ciclo el valor de result es igual a 3 y el elemento actual de la secuencia es 7 por lo que en esta occasion al closure se le llama con los parámetros (3, 7) y así:

Swift - Funciones de Órden Superior - WeiseRatel
Comportamiento de la función reduce

consecutivamente hasta llegar al último elemento y donde el resultado final se retorna y almacena en este caso en la constante sum.

FlatMap

Digamos que tenemos un arreglo que contiene a su vez dos arreglos más dentro de este y queremos «aplanarlo», es decir deseamos tener un solo arreglo que contenga los valores de estos dos. Nuevamente un bucle for nos ayudaría con esta tarea:

let arrayInArray = [ [11, 12, 13], [14, 15, 16] ]

var emptyArray: [Int] = []

for array in arrayInArray {
    
    emptyArray += array

} // for

print(emptyArray)

pero con flatMap en una línea logramos lo mismo:

let arrayInArray = [ [11, 12, 13], [14, 15, 16] ]

let flatArray = arrayInArray.flatMap { $0 }

print(flatArray)

la salida en pantalla es:

[11, 12, 13, 14, 15, 16]

Una vez más creo que es evidente el ahorro de tiempo en esta segunda versión del código.

Antes de continuar me gustaría puntualizar algo para que no caigan ante la misma confusión en la que se encontró Natasha The Robot al trabajar con flatMap (en Inglés).

[better-ads type=’banner’ banner=’161′ ]

Resulta que cuando especificamos $0 dentro de las llaves de flatMap no estamos haciendo referencia a cada elemento de los array como sucede en map.

No, cuando hacemos esto nos referimos a cada arreglo dentro del arreglo padre por lo que si quisiéramos sumar dos a cada entero tendríamos que hacer lo siguiente:

let arrayInArray = [ [11, 12, 13], [14, 15, 16] ]

let flatArray = arrayInArray.flatMap{ $0.map { $0 + 2 } }

print(flatArray)

la nueva salida en pantalla es:

[13, 14, 15, 16, 17, 18]

Dicho lo anterior si $0 referencia a un arreglo pues entonces podemos hacer uso de map y es dentro de este donde le sumamos dos unidades a cada elemento, luego cuando flatMap pase al segundo arreglo pues se continuúa con la operación de suma hasta llegar al final.

Otra característica o funcionalidad digna de comentar es aquella donde eliminamos todos los elementos nil del arreglo final, veamos un ejemplo:

let arrayInArray: [Int?] = [ 11, 12, nil, 14, 15, 16 ]

let flatArray = arrayInArray.flatMap{ $0.map { $0 + 2 } }

print(flatArray)

Este código es válido (aunque no tenga otros arreglo anidados y creamos que eventualmente intentaremos sumar dos a nil) ya que flatMap obvia el procesamiento del valor nil. Así que:

[13, 14, 16, 17, 18]

la salida en pantalla será de solo 5 elementos.

Prefix

De un arreglo la función prefix nos devuelve la secuencia más larga que satisfaga cierto patrón, comienza por el inicio de la secuencia y se detiene solamente cuando encuentra por primera vez un elemento por el cual el closure especificado nos devuelve el valor booleano false, es decir cuando no cumple con cierta condición.

Veamos un ejemplo sencillo:

let someNumbers = [1, 2, 3, 4, 5, 6, 7, 1, 0, 8]

let prefixed = someNumbers.prefix(while: { (element) -> Bool in element < 5 } )

print(prefixed)

En este código comenzamos por crear un arreglo de enteros, seguido a esto creamos otra constante pero con los valores devueltos por la ejecución de un closure sobre la función prefix que a su vez referenciamos en el arreglo de nombre someNumbers.

¿Cuál creen que será la salida en pantalla?

En el closure la expresión evalúa si el elemento es menor (<) que 5, por lo que la función prefix retornará algún valor siempre y cuando esta condición se cumpla desde el inicio del arreglo. La salida en pantalla es la siguiente:

[1, 2, 3, 4]

Al llegar al quinto elemento y determinarse que no es menor a 5 pues la expresión dentro del closure devuelve false y la función prefix culmina su ejecución retornando los valor recorridos hasta ese momento.

Si este ejemplo lo modificamos :

let someNumbers = [1, 2, 3, 4, 5, 6, 7, 1, 0, 8]

let prefixed = someNumbers.prefix { $0 < 0 }

print(prefixed)

nos devuelve:

[]

En esta versión hemos usado la sintaxis simplificada de los closure que es la que personalmente prefiero. Ahora, el código anterior no retorna valor alguno ya que ninguno de los elementos del arreglo es menos a 0 por lo que la condición jamás es false.

Drop

La función drop es muy similar a prefix, la diferencia reside en que drop se comporta de manera contraria a prefix.

En palabras más claras, drop recorre una secuencia de valores dentro de un arreglo de inicio a fin. Evalúa sobre estos elementos la condición pasada a través del closure, pero a diferencia de prefix, cuando esta condición devuelve false por primera vez su ejecución no termina.

Es en este punto donde comienza a capturar los valores que luego retornará al llegar al final de la secuencia.

Veamos el anterior ejemplo modificado:

let someNumbers = [1, 2, 3, 4, 5, 6, 7, 1, 0, 8]

let dropped = someNumbers.drop(while: { (element) -> Bool in element == 9 } )

print(dropped)

Ahora la condición es que el elemento sea igual a 9 que no está en el arreglo ni tampoco es el primer elemento por lo que ya desde el inicio la expresión es false, así que drop devuelve todos los elementos:

[1, 2, 3, 4, 5, 6, 7, 1, 0, 8]

Si cambiamos la condición:

let someNumbers = [1, 2, 3, 4, 5, 6, 7, 1, 0, 8]

let dropped = someNumbers.drop { $0 > 0 }

print(dropped)

entonces todos los elementos del inicio del arreglo devolverán true hasta llegar al penúltimo que es 0, la salida ahora sería:

[0, 8]

En este último ejemplo hemos hecho lo mismo, pasamos de la sintaxis estándar del closure a usar una más simplificada pero aún legible. Esto lo hago para recordar que los closures tienen una estructura más compleja donde se muestran todos los elementos que lo conforman.

Es bien importante estar familiarizados con toda la variedad de sus formas. Creo que se ha entendido perfectamente, ¿cierto?

Encadenamiento de Funciones

En este punto es donde realmente las funciones de orden superior realmente destacan. Sí amigos, las podemos encadenar y lograr comportamientos muy interesantes. Digamos que necesitamos calcular la raíz cuadrada de todos los números pares de un arreglo de arreglos:

let arrayInArray = [[11, 12, 13], [14, 15, 16]]

var newArray = arrayInArray.flatMap { $0 }.filter { $0 % 2 == 0}.map { $0 * $0 }

print(newArray)

En la tercera línea hemos utilizado tres funciones de orden superior concatenadas en función y orden de nuestro objetivo. Lo primero que hacemos es llamar a flatMap en pos de unificar los arreglos que hayan en uno solo.

Seguido a esto filtramos los números pares y por último a estos le calculamos el cuadrado. El nuevo arreglo se almacena en newArray con los siguientes valores:

[144, 196, 256]

valores que pudiéramos sumar, si así fuese necesario, solamente con agregarle reduce:

var newValue = arrayInArray.flatMap { $0 }.filter { $0 % 2 == 0}.map { $0 * $0 }.reduce(0, +)

En este punto si te preguntas que quiere decir (0, +) pues te comento que esta sería la forma más simplificada del closure correspondiente al segundo parámetro.

He usado esta variante con intenciones puramente didácticas ya que me luce demasiado críptica y poco legible. Yo prefiero la siguiente variante:

var newValue = arrayInArray.flatMap { $0 }.filter { $0 % 2 == 0}.map { $0 * $0 }.reduce(0, { $0 + $1 } )

donde el closure es mucho más explícito en su operación. La salida en pantalla de este código sería:

596

¿Por qué usar Funciones de Orden Superior?

Los ejemplos que hemos visto son una pista más que clara de los beneficios que nos brindan las funciones de orden superior sobre el uso de los métodos clásicos, con estas escribimos códigos más elegantes y fáciles de mantener que en la mayoría de los casos también se traduce en una mejor legibilidad.

Creo que el punto más importante sería el habituarnos en el uso de los closures y eventualmente la implementación de nuestras propias funciones de orden superior, esto con el tiempo nos ayudará a leer y a comprender mucho mejor la programación funcional.

Falta aún mucho por aprender en nuestro camino a convertirnos en iOS Developer. Suscríbete a nuestra lista de correo y síguenos en nuestras redes sociales. Mantente al tanto de todas nuestras publicaciones.

Espero que todo cuanto se ha dicho aquí, de una forma u otra le haya servido de aprendizaje, de referencia, que haya valido su preciado tiempo.

Este artículo, al igual que el resto, será revisado con cierta frecuencia en pos de mantener un contenido de calidad y actualizado.

¡Cualquier duda o sugerencia, ya sea errores a corregir o ejemplos a añadir, será más que bienvenida, necesaria!

WeiseRatel

WeiseRatel

Desarrollador Swift / iOS. Llevo en el mundo de la informática más de 15 años, trabajado con varios lenguajes de programación y frameworks como Swift, C++ / Qt, C#, Java, entre otros. También he fungido como administrador de redes, bases de datos y sistemas Linux (algún que otro BSD) desde 2005 hasta la actualidad. Soy “Experto en Administración y Seguridad de Redes” por la Universidad Tecnológica Nacional FRVM de Córdoba, Argentina.

Artículos relacionados

La Sentencia Guard, Swift
Swift

Swift – La Sentencia Guard

¿Cómo iterar sobre tipos de datos propios?, Swift
Swift

Swift – ¿Cómo iterar sobre tipos de datos propios?

Trabajo con Fechas, Swift
Swift

Swift – Trabajo con Fechas

Gestión de Archivos, Swift
Swift

Swift – Gestión de Archivos

Load More
0 0 votos
Article Rating
Suscríbete
Login
Notificar de
guest
guest

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.

0 Comments
Inline Feedbacks
Ver todos los comentarios

Suscríbete

Únete!

Únete!

Suscríbete a nuestra lista de correo y mantente actualizado con las nuevas publicaciones.

Se ha suscrito correctamente!

Entradas recientes

  • Estructuras de Datos – Lista Enlazada
  • MariaDB – Comandos Básicos
  • MariaDB – Instalación y Configuración
  • ¿Cómo hacer una instalación limpia de macOS Catalina?
  • Git – Instalación y Configuración

Categorías

  • Bases de Datos (2)
    • MariaDB (2)
  • Estructuras de Datos (3)
  • Gestión de Dependencias (2)
    • Swift Package Manager (2)
  • Git (2)
  • Guías (2)
  • Lenguajes de Programación (27)
    • Swift (27)
  • Sobre WiseRatel.com
  • Política de Privacidad
  • Política de Cookies
  • ¿Hablamos?
Powered by humans!

© 2021 WeiseRatel. All Rights Reserved.

No Result
View All Result
  • ¿Qué es Swift?
  • ¿Hablamos?
  • Sobre WeiseRatel.com

© 2021 WeiseRatel. All Rights Reserved.

wpDiscuz
RECIBE CONTENIDO SIMILAR EN TU CORREO

RECIBE CONTENIDO SIMILAR EN TU CORREO

Suscríbete a nuestra lista de correo y mantente actualizado con las nuevas publicaciones.

Se ha suscrito correctamente!

Cookies Para que este sitio funcione adecuadamente, a veces instalamos en los dispositivos de los usuarios pequeños ficheros de datos, conocidos como cookies. La mayoría de los grandes sitios web también lo hacen.
Aceptar cookies
Leer más
Ajustes de cookies
Configuración de Cookie Box
Configuración de Cookie Box

Ajustes de privacidad

Decida qué cookies desea permitir. Puede cambiar estos ajustes en cualquier momento. Sin embargo, esto puede hacer que algunas funciones dejen de estar disponibles. Para obtener información sobre eliminar las cookies, por favor consulte la función de ayuda de su navegador. MÁS INFORMACIÓN SOBRE LAS COOKIES QUE USAMOS.

Con el deslizador, puede habilitar o deshabilitar los diferentes tipos de cookies:

  • Block all
  • Essential
  • Functionality
  • Analytics
  • Advertising

Este sitio web

  • Esencial: Recordar sus ajustes de permisos de cookies
  • Esencial: Permitir cookies de sesión
  • Esencial: Recopilar la información que introduzca en el formulario de contacto de un boletín informativo y otros formularios en todas las páginas
  • Esencial: Hacer un seguimiento de lo que introduzca en la cesta de la compra
  • Esencial: Verificar que ha iniciado sesión en su cuenta de usuario
  • Esencial: Recordar la versión del idioma seleccionado

Este sitio web no

  • Recordar los datos de inicio de sesión
  • Funcionalidad: Recordar los ajustes de redes sociales
  • Funcionalidad: Recordar el país y la región seleccionados
  • Análisis: Hacer un seguimiento de las páginas visitadas y de la interacción en las mismas
  • Análisis: Hacer un seguimiento de la ubicación y la región según la dirección IP
  • Análisis: Hacer un seguimiento del tiempo pasado en cada página
  • Análisis: Aumentar la calidad de los datos de las funciones estadísticas
  • Publicidad: Adaptar la información y la publicidad a sus intereses según, p.ej., el contenido que ha visitado antes. (Actualmente no usamos segmentación ni cookies de segmentación)
  • Publicidad: Recopilar información personalmente identificable como el nombre y la ubicación

Este sitio web

  • Esencial: Recordar sus ajustes de permisos de cookies
  • Esencial: Permitir cookies de sesión
  • Esencial: Recopilar la información que introduzca en el formulario de contacto de un boletín informativo y otros formularios en todas las páginas
  • Esencial: Hacer un seguimiento de lo que introduzca en la cesta de la compra
  • Esencial: Verificar que ha iniciado sesión en su cuenta de usuario
  • Esencial: Recordar la versión del idioma seleccionado
  • Funcionalidad: Recordar todos los ajustes de redes sociales
  • Funcionalidad: Recordar el país y la región seleccionados

Este sitio web no

  • Análisis: Hacer un seguimiento de las páginas visitadas y de la interacción en las mismas
  • Análisis: Hacer un seguimiento de la ubicación y la región según la dirección IP
  • Análisis: Hacer un seguimiento del tiempo pasado en cada página
  • Análisis: Aumentar la calidad de los datos de las funciones estadísticas
  • Publicidad: Adaptar la información y la publicidad a sus intereses según, p.ej., el contenido que ha visitado antes. (Actualmente no usamos segmentación ni cookies de segmentación)
  • Publicidad: Recopilar información personalmente identificable como el nombre y la ubicación

Este sitio web

  • Esencial: Recordar sus ajustes de permisos de cookies
  • Esencial: Permitir cookies de sesión
  • Esencial: Recopilar la información que introduzca en el formulario de contacto de un boletín informativo y otros formularios en todas las páginas
  • Esencial: Hacer un seguimiento de lo que introduzca en la cesta de la compra
  • Esencial: Verificar que ha iniciado sesión en su cuenta de usuario
  • Esencial: Recordar la versión del idioma seleccionado
  • Funcionalidad: Recordar todos los ajustes de redes sociales
  • Funcionalidad: Recordar el país y la región seleccionados
  • Análisis: Hacer un seguimiento de las páginas visitadas y de la interacción en las mismas
  • Análisis: Hacer un seguimiento de la ubicación y la región según la dirección IP
  • Análisis: Hacer un seguimiento del tiempo pasado en cada página
  • Análisis: Aumentar la calidad de los datos de las funciones estadísticas

Este sitio web no

  • Publicidad: Adaptar la información y la publicidad a sus intereses según, p.ej., el contenido que ha visitado antes. (Actualmente no usamos segmentación ni cookies de segmentación)
  • Publicidad: Recopilar información personalmente identificable como el nombre y la ubicación

Este sitio web

  • Funcionalidad: Recordar los ajustes de redes sociales
  • Funcionalidad: Recordar el país y la región seleccionados
  • Análisis: Hacer un seguimiento de las páginas visitadas y de la interacción en las mismas
  • Análisis: Hacer un seguimiento de la ubicación y la región según la dirección IP
  • Análisis: Hacer un seguimiento del tiempo pasado en cada página
  • Análisis: Aumentar la calidad de los datos de las funciones estadísticas
  • Publicidad: Adaptar la información y la publicidad a sus intereses según, p.ej., el contenido que ha visitado antes. (Actualmente no usamos segmentación ni cookies de segmentación)
  • Publicidad: Recopilar información personalmente identificable como el nombre y la ubicación

Este sitio web no

  • Recordar los datos de inicio de sesión
Guardar cerrar