JAVASCRIPT 101. FUNCIONES DE ORDEN SUPERIOR

< script src=”my own” title=”javascript 101. funciones de orden superior” >

En la teoría formal de funciones, aquellas que son de Orden Superior, son aquellas funciones que apuntan a un subconjunto de datos generado por otra(s) funcion(es), o cuyo origen es un subconjunto de datos de la misma naturaleza. Esto en programación funcional no es más que una función que puede recibir otras funciones como parámetros o que puede retornar una función. Esto es una característica muy aprovechable en lenguajes de programación funcionales como Javascript 🙄 .

Espero hayas estudiado bastante, es hora de un examen… Aqui tienes 😈

Da como miedito... :(

Da como miedito… 😦

Y pues bueno, como estudiamos bastante Javascript ( 😕 ehm…), pero no tenemos ni idea de que va el rollo de las Funciones de Orden Superior, lo hacemos a la manera en que conocemos, la forma Vanilla JS XD :

/* Se obtiene la frase */
var frase = document.querySelector('#trabalenguas').innerHTML;

/* Se separa la frase por espacios (signos de puntuación incluídos)*/
var arrayPalabras = frase.split(' ');

console.time('Forma 1. Vanilla JS');
var aux = []; //Auxiliar para guardar las palabras sin signos de puntuación
arrayPalabras.forEach(function(palabra) {
	/* Se eliminan los signos de puntuación */
	aux.push(palabra.replace(/(\w+)[\.,]?/i, '$1'));
});
arrayPalabras = aux; // Array limpio

var arrayMayusculas = [];
arrayPalabras.forEach(function(palabra) {
	/* Cada palabra a mayúsculas */
	arrayMayusculas.push(palabra.toUpperCase());
});

console.log('Respuesta 1:', arrayMayusculas);

var contador = {}; //"Mapa" de palabras a contar
arrayPalabras.forEach(function(palabra) {
	/* Cada palabra a minúsculas para facilitar conteo */
	palabra = palabra.toLowerCase();
	/* notación "Array" de "Object". Validando que el atributo exista */
	if (typeof contador[palabra] !== 'undefined') {
		contador[palabra]++; // conteo único por atributo dentro del objeto
	} else {
		contador[palabra] = 1; // se crea el atributo dentro del objeto
	}
});

console.log('Respuesta 2:', contador);

var arraySeleccion = [];
arrayPalabras.forEach(function(palabra) {
	/* true en palabras como "Casa" o "casa" */
	if (/^[Cc]/.test(palabra))
		arraySeleccion.push(palabra);
});

arraySeleccion.sort(); // Se ordena el Array de forma ascendente

var i = 0;
while(i &lt; arraySeleccion.length){ // Búsqueda lineal
	/* true si es idéntica a siguiente posición */
	if(arraySeleccion[i] === arraySeleccion[i + 1])
		arraySeleccion.splice(i--, 1); // Se elimina posición anterior
	i++;
}

console.log('Respuesta 3:', arraySeleccion);
console.timeEnd('Forma 1. Vanilla JS');

¡Voilá! Un “poco trivial”, ¿eh? ^^’ Primero veamos los resultados y luego explico un poco de que va el post, claro:

¿6.26milisegundos? Nada mal ¿eh? :D

¿6.26 milisegundos? Nada mal ¿eh? 😀

console.time() es un objeto del navegador que sirve para hacer profiling básico (cuanto tiempo tarda tu código en ejecutarse), para mas detalles puedes visitar este enlace. Pero el meollo no es ese, sino explicar la ventaja de usar funciones de orden superior. La función forEach() es un ejemplo claro de estas funciones: recibe una función como parámetro que será ejecutada por cada elemento existente dentro del Array, es decir, no se ejecutará sobre los valores null, NaN, cadenas vacías o undefined; nos queda de perlas para lo que queremos hacer y tiene un rendimiento decente si consideramos que son pocas líneas de código, pero la cosa se complica. Ahora imagínate que la prueba cambia drásticamente:

¿¡ perdón !? o_O

¿¡ perdón !? o_O

Afortunadamente, Javascript cuenta con muchas más funciones de orden superior que podemos usar. Mmm, digamos que ahora si estudiamos de verdad XD

/* Se obtiene la frase */
var frase = document.querySelector('#trabalenguas').innerHTML;

/* Se separa la frase por espacios (signos de puntuación incluídos)*/
var arrayPalabras = frase.split(' ');

console.time('Forma 2. Funciones de orden superior');
/* Array.map() ejecuta la función dada como parámetro sobre cada elemento del Array.
 * Retorna: Un nuevo arreglo
 * Utilidad: Transformar los valores del arreglo en un nuevo formato
 */
arrayPalabras = arrayPalabras.map(function(palabra){
	return palabra.replace(/(\w+)[\.,]?/i, '$1');
});

var arrayMayusculas = arrayPalabras.map(function(palabra){
	return palabra.toUpperCase();
});

console.log('Respuesta 1:', arrayMayusculas);

/* Array.reduce() ejecuta la función dada como parámetro sobre cada elemento del Array,
 * con la peculiaridad de que el valor se va acumulando de acuerdo con la definición de 
 * la función dada
 * Retorna: Una colección con el(los) valor(es) acumulado(s) (Object o Array)
 * Utilidad: Transformaciones complejas sobre Arrays o totalización de valores
 */
var contador = arrayPalabras.reduce(function(contador, palabra){
	palabra = palabra.toLowerCase();
	contador[palabra] = (+contador[palabra] || 0) + 1;
	return contador;
}, {});

console.log('Respuesta 2:', contador);

/* Array.filter() ejecuta la función dada como parámetro sobre cada elemento del Array.
 * Retorna: Un Array con los valores que resulten true de la evaluación
 * Utilidad: Selección de valores de un Array según una condición dada
 */
var arraySeleccion = arrayPalabras.filter(function(palabra) {
	return /^[Cc]/.test(palabra);
});

/* Ejemplo de Array.reduce() que devuelve un Array */
arraySeleccion = arraySeleccion.reduce(function(arrayUnico, palabra) {
	if(arrayUnico.indexOf(palabra) === -1) {
		arrayUnico.push(palabra);
	}
	return arrayUnico;
}, []);

console.log('Respuesta 3:', arraySeleccion);
console.timeEnd('Forma 2. Funciones de orden superior');

Ya no es tan trivial como en el primer caso. La sintaxis es realmente compleja y un poco “truculenta” de entender si no entiendes los conceptos tras bastidores de la programación funcional, pero siempre puedes visitar la documentación de map(), reduce(), filter(), o de otras funciones de las que dispones en Javascript.

Aunque sea difícil de entender al principio, tiene su recompensa :mrgreen:

¿3.23 milisegundos? ¡Whoa! :o

¿3.63 milisegundos? ¡Whoa! 😮

Conclusión

Usar o no usar funciones de orden superior… No es una cuestión trivial ni sencilla de resolver…

Por un lado, usar Vanilla JS siempre es más simple y razonablemente comprensible para depuraciones futuras, sin embargo, más líneas de código implican mayor consumo de memoria, reduciendo el rendimiento de tu aplicación. Mientras que usando funciones de orden superior obtienes un rendimiento visiblemente mejorado, comparado con un procesamiento trocho y mocho, a expensas de dejar un código un poco ofuscado para aquellos que no conocen aspectos básicos de programación funcional, reduciendo la posibilidad de ser mantenible o depurable.

¿Qué elegirías tú? 😉

</script>

Anuncios