Eche un vistazo a los siguientes fragmentos de código que resuelven el mismo problema y piense cuál le gusta más.
Aquí está el primero: | Aquí está el segundo: |
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(int => isEven(int)) .filter(int => isBiggerThan(3, int)) .map(int => int + 1) .map(int => toChar(int)) .filter(char => !isVowel(char)) .join('') // 'fhjl'
| [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('') |
"Apuesto a que la segunda opción es mucho mejor legible que la primera", dice el autor del material, cuya traducción publicamos hoy. Según él, todo el punto está en los argumentos de los métodos
filter()
y
map()
.

Hoy hablaremos sobre cómo reciclar código similar al primer ejemplo para que se vea como el código del segundo. El autor del artículo promete que, una vez que comprenda cómo funciona, se relacionará con sus programas de una manera nueva y no podrá ignorar lo que solía parecer bastante normal y no requiere mejoras.
Función simple
Considere una función simple
sum()
que agrega los números que se le pasan:
const sum = (a, b) => a + b sum(1, 2)
Lo reescribimos, dando a la nueva función el nombre
csum()
:
const csum = a => b => a + b csum(1)(2)
Su nueva versión funciona exactamente de la misma manera que la original, la única diferencia es cómo se llama esta nueva función. A saber, la función
sum()
toma dos parámetros a la vez, y
csum()
toma los mismos parámetros uno a la vez. De hecho, cuando se llama a
csum()
, se llaman dos funciones. En particular, considere la situación cuando
csum()
llama a
csum()
, pasándole el número 1 y nada más:
csum(1)
Tal llamada a
csum()
lleva al hecho de que devuelve una función que puede aceptar el segundo argumento numérico pasado a
csum()
durante su llamada habitual, y devuelve el resultado de agregar uno a este argumento. Llame a esta función
plusOne()
:
const plusOne = csum(1) plusOne(2)
Trabajar con matrices
En JavaScript, puede trabajar con matrices utilizando una variedad de métodos especiales. Digamos que el método
map()
se usa para aplicar la función que se le pasa a cada elemento de la matriz.
Por ejemplo, para aumentar en 1 cada elemento de una matriz entera (más precisamente, para formar una nueva matriz que contiene elementos de la matriz original aumentada en 1), puede usar la siguiente construcción:
[1, 2, 3].map(x => x + 1)
En otras palabras, lo que está sucediendo se puede describir de la siguiente manera: la función
x => x + 1
toma un número entero y devuelve el número que le sigue en una serie de números enteros. Usando la función
plusOne()
discutida anteriormente, este ejemplo puede reescribirse de la siguiente manera:
[1, 2, 3].map(x => plusOne(x))
Aquí vale la pena reducir la velocidad y pensar en lo que está sucediendo. Si hace esto, puede ver que en el caso considerado, las construcciones
x => plusOne(x)
y
plusOne
(tenga en cuenta que en esta situación no hay corchetes después del nombre de la función) son equivalentes. Para comprender esto mejor, considere la función
otherPlusOne()
:
const otherPlusOne = x => plusOne(x) otherPlusOne(1)
El resultado de esta función será el mismo que el obtenido por una simple llamada al
plusOne()
ya conocemos:
plusOne(1)
Por la misma razón, podemos hablar sobre la equivalencia de las siguientes dos construcciones. Aquí está el primero que ya hemos visto:
[1, 2, 3].map(x => plusOne(x))
Aquí está el segundo:
[1, 2, 3].map(plusOne)
Además, recuerde cómo se creó la función
plusOne()
:
const plusOne = csum(1)
Esto nos permite reescribir nuestra construcción con
map()
siguiente manera:
[1, 2, 3].map(csum(1))
Ahora, usando la misma técnica,
isBiggerThan()
función
isBiggerThan()
. Si lo desea, intente hacerlo usted mismo y luego siga leyendo. Esto eliminará el uso de construcciones innecesarias al usar el método
filter()
. Primero, traigamos el código a este formulario:
const isBiggerThan = (threshold, int) => int > threshold [1, 2, 3, 4].filter(int => isBiggerThan(3, int))
Luego, al deshacernos de todo lo superfluo, obtenemos el código que ya vio al comienzo de este material:
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] .filter(isEven) .filter(isBiggerThan(3)) .map(plus(1)) .map(toChar) .filter(not(isVowel)) .join('')
Ahora consideramos dos reglas simples que le permiten escribir código en el estilo discutido aquí.
Regla número 1
Las siguientes dos construcciones son equivalentes:
[…].map(x => fnc(x)) […].map(fnc)
Regla número 2
Las devoluciones de llamada siempre se pueden reescribir para reducir la cantidad de argumentos utilizados para llamarlo:
const fnc = (x, y, z) => … […].map(x => fnc(x, y, z)) const fnc = (y, z) => x => … […].map(fnc(y, z))
Si usted mismo escribió la función
isBiggerThan()
, entonces probablemente ya haya recurrido a dicha transformación. Supongamos que necesitamos números mayores que 3 para pasar a través de un filtro. Esto se puede hacer así:
const isBiggerThan = (threshold, int) => int > threshold […].filter(int => isBiggerThan(3, int))
Ahora reescribimos la función
isBiggerThan()
para que pueda usarse en el método
filter()
y no usar la construcción
int=>
:
const isBiggerThan = threshold => int => int > threshold […].map(isBiggerThan(3))
Ejercicio
Supongamos que tenemos el siguiente fragmento de código:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 keepGreatestChar('b', 'f') // 'f' // 'f' 'b'
Ahora, basado en la función
keepGreatestChar()
, cree la función
keepGreatestCharBetweenBAnd()
. Necesitamos que, al llamarlo, podamos pasarle solo un argumento, mientras compararemos el carácter pasado con el carácter
b
. Esta función puede verse así:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const keepGreatestCharBetweenBAnd = char => keepGreatestChar('b', char) keepGreatestCharBetweenBAnd('a')
Ahora escriba la función
greatestCharInArray()
, que, utilizando la función
keepGreatestChar()
en el método de matriz
reduce()
, le permite buscar el carácter "más grande" y no necesita argumentos. Comencemos con este código:
const keepGreatestChar = (char1, char2) => char1 > char2 ? char1 : char2 const greatestCharInArray = array => array.reduce((acc, char) => acc > char ? acc : char, 'a') greatestCharInArray(['a', 'b', 'c', 'd'])
Para resolver este problema, implemente la función
creduce()
, que se puede usar en la función
creduce()
, que permite, en la aplicación práctica de esta función, no pasarle nada excepto la matriz en la que se encuentra el carácter con el código más grande.
La función
creduce()
debe ser lo suficientemente universal como para que pueda usarse para resolver cualquier problema que requiera el uso de las capacidades del método de matriz
reduce()
estándar. En otras palabras, la función debe tomar una devolución de llamada, un valor inicial y una matriz para trabajar. Como resultado, debe obtener una función con la que funcionará el siguiente fragmento de código:
const greatestCharInArray = creduce(keepGreatestChar, 'a') greatestCharInArray(['a', 'b', 'c', 'd'])
Resumen
Quizás ahora tenga una pregunta sobre por qué los métodos, procesados de acuerdo con la metodología presentada aquí, tienen nombres que comienzan con el carácter
c
. El carácter
c
es un acrónimo de curry, y hablamos sobre cómo las funciones curry ayudan a mejorar la legibilidad del código. Cabe señalar que aquí no nos esforzamos por cumplir estrictamente los principios de la programación funcional, pero creemos que la aplicación práctica de lo que se discutió aquí nos permite mejorar el código. Si el tema de curry en JavaScript es interesante para usted, se recomienda leer el cuarto capítulo de
este libro sobre programación funcional y, en general, desde que llegó a este punto, lea todo este libro. Además, si es nuevo en la programación funcional, consulte
este material de inicio.
Estimados lectores! ¿Utiliza función curry en el desarrollo de JavaScript?
