No tengas miedo de la programación funcional

 

 

 


Índice
  1. La programación funcional puede resultar abrumadora
  2. Seamos realistas
    1. Otras lecturas

Los lenguajes de programación funcionales más estrictos se utilizan normalmente cuando el rendimiento y la integridad de un sistema son críticos, es decir, su programa necesita hacer exactamente lo que usted espera cada vez y necesita operar en un entorno donde sus tareas se pueden compartir entre cientos o miles de computadoras en red. Estos lenguajes tienen una curva de aprendizaje pronunciada para la mayoría de los desarrolladores web front-end; sin embargo, muchos lenguajes más accesibles incorporan características de programación funcional.

 

La programación funcional es el hipster bigotudo de los paradigmas de programación. Originalmente relegada a los anales de la academia de ciencias de la computación, la programación funcional ha tenido un renacimiento reciente que se debe en gran medida a su utilidad en sistemas distribuidos (y probablemente también a que los lenguajes funcionales “puros” como Haskell son difíciles de entender, lo que les da cierto prestigio). ).

Los lenguajes de programación funcionales más estrictos se utilizan normalmente cuando el rendimiento y la integridad de un sistema son críticos, es decir, su programa necesita hacer exactamente lo que usted espera cada vez y necesita operar en un entorno donde sus tareas se pueden compartir entre cientos o miles de computadoras en red.

Clojure , por ejemplo, impulsa a Akamai , la red de distribución masiva de contenidos utilizada por empresas como Facebook, mientras que Twitter adoptó Scala para sus componentes con mayor rendimiento intensivo, y ATT utiliza Haskell para sus sistemas de seguridad de red.

Estos lenguajes tienen una curva de aprendizaje pronunciada para la mayoría de los desarrolladores web front-end; sin embargo, muchos lenguajes más accesibles incorporan características de programación funcional, sobre todo Python, tanto en su biblioteca principal, con funciones como mapy reduce(de las que hablaremos más adelante), como con bibliotecas como Fn.py , junto con JavaScript. , nuevamente usando métodos de recopilación, pero también con bibliotecas como Underscore.js y Bacon.js .

La programación funcional puede resultar abrumadora

Pero recuerde que no es sólo para doctores, científicos de datos y astronautas de la arquitectura. Para la mayoría de nosotros, el beneficio real de adoptar un estilo funcional es que nuestros programas se pueden dividir en partes más pequeñas y simples que son más confiables y más fáciles de entender. Si es un desarrollador front-end que trabaja con datos, especialmente si está formateando esos datos para su visualización usando D3, Raphael o similares, entonces la programación funcional será un arma esencial en su arsenal.

 

Encontrar una definición coherente de programación funcional es difícil, y la mayor parte de la literatura se basa en afirmaciones un tanto aprensivos como "funciona como objetos de primera clase" y "eliminación de efectos secundarios". En caso de que eso no te haga un nudo en el cerebro, en un nivel más teórico, la programación funcional a menudo se explica en términos de cálculo lambda (algunos incluso argumentan que la programación funcional es básicamente matemática), pero puedes relajarte. Desde una perspectiva más pragmática, un principiante necesita comprender sólo dos conceptos para poder utilizarlo en aplicaciones cotidianas (¡no se requieren cálculos!).

En primer lugar, los datos de los programas funcionales deben ser inmutables , lo que suena serio pero simplemente significa que nunca deberían cambiar. Al principio, esto puede parecer extraño (después de todo, ¿quién necesita un programa que nunca cambie nada?), pero en la práctica, simplemente se crearían nuevas estructuras de datos en lugar de modificar las que ya existen. Por ejemplo, si necesita manipular algunos datos en una matriz, entonces crearía una nueva matriz con los valores actualizados, en lugar de revisar la matriz original. ¡Fácil!

En segundo lugar, los programas funcionales deben ser sin estado , lo que básicamente significa que deben realizar cada tarea como si fuera la primera vez, sin conocimiento de lo que pudo o no haber sucedido anteriormente en la ejecución del programa (se podría decir que un programa sin estado ignora el pasado). Combinado con la inmutabilidad, esto nos ayuda a pensar en cada función como si estuviera operando en el vacío, felizmente ignorante de cualquier otra cosa en la aplicación además de otras funciones. En términos más concretos, esto significa que sus funciones operarán solo con datos pasados ​​como argumentos y nunca dependerán de valores externos para realizar sus cálculos.

La inmutabilidad y la apatridia son fundamentales para la programación funcional y es importante comprenderlas, pero no se preocupe si aún no tienen sentido. Estará familiarizado con estos principios al final del artículo y le prometo que la belleza, la precisión y el poder de la programación funcional convertirán sus aplicaciones en brillantes y relucientes arcoíris devoradores de datos. Por ahora, comience con funciones simples que devuelvan datos (u otras funciones) y luego combine esos componentes básicos para realizar tareas más complejas.

Por ejemplo, digamos que tenemos una respuesta API:

var data = [ { name: "Jamestown", population: 2047, temperatures: [-34, 67, 101, 87] }, { name: "Awesome Town", population: 3568, temperatures: [-3, 4, 9, 12] } { name: "Funky Town", population: 1000000, temperatures: [75, 75, 75, 75, 75] }];

Si queremos usar una tabla o una biblioteca de gráficos para comparar la temperatura promedio con el tamaño de la población, necesitaríamos escribir algo de JavaScript que realice algunos cambios en los datos antes de que se formatee correctamente para nuestra visualización. Nuestra biblioteca de gráficos quiere una matriz de coordenadas xey, así:

 

[ [x, y], [x, y] …etc]

Aquí xestá la temperatura promedio y yel tamaño de la población.

Sin programación funcional (o sin usar lo que se llama un estilo “imperativo”), nuestro programa podría verse así:

var coords = [], totalTemperature = 0, averageTemperature = 0;for (var i=0; i data.length; i++) { totalTemperature = 0; for (var j=0; j data[i].temperatures.length; j++) { totalTemperature += data[i].temperatures[j]; } averageTemperature = totalTemperature / data[i].temperatures.length; coords.push([averageTemperature, data[i].population]);}

Incluso en un ejemplo artificial, esto ya resulta difícil de seguir. Veamos si podemos hacerlo mejor.

Cuando se programa en un estilo funcional, siempre se buscan acciones simples y repetibles que puedan abstraerse en una función. Luego podemos crear funciones más complejas llamando a estas funciones en secuencia (también conocidas como funciones de "composición"); hablaremos más de eso en un segundo. Mientras tanto, veamos los pasos que daríamos en el proceso de transformar la respuesta API inicial a la estructura requerida por nuestra biblioteca de visualización. A nivel básico realizaremos las siguientes acciones sobre nuestros datos:

  • agregar cada número en una lista,
  • calcular un promedio,
  • recuperar una sola propiedad de una lista de objetos.

Escribiremos una función para cada una de estas tres acciones básicas y luego componeremos nuestro programa a partir de esas funciones. La programación funcional puede resultar un poco confusa al principio y probablemente se sentirá tentado a caer en viejos hábitos imperativos. Para evitarlo, aquí hay algunas reglas básicas simples que le permitirán asegurarse de seguir las mejores prácticas:

  1. Todas sus funciones deben aceptar al menos un argumento.
  2. Todas sus funciones deben devolver datos u otra función.
  3. ¡Sin bucles!

Bien, agreguemos todos los números de una lista. Recordando las reglas, asegurémonos de que nuestra función acepte un argumento (la matriz de números a sumar) y devuelva algunos datos.

function totalForArray(arr) { // add everything return total; }

Hasta ahora, todo bien. Pero, ¿cómo vamos a acceder a cada elemento de la lista si no lo repasamos? ¡Saluda a tu nuevo amigo, la recursividad! Esto es un poco complicado, pero básicamente, cuando usas la recursividad, creas una función que se llama a sí misma a menos que se cumpla una condición específica, en cuyo caso, se devuelve un valor. Probablemente sea más fácil mirar un ejemplo:

// Notice we're accepting two values, the list and the current totalfunction totalForArray(currentTotal, arr) { currentTotal += arr[0]; // Note to experienced JavaScript programmers, I'm not using Array.shift on // purpose because we're treating arrays as if they are immutable. var remainingList = arr.slice(1); // This function calls itself with the remainder of the list, and the // current value of the currentTotal variable if(remainingList.length 0) { return totalForArray(currentTotal, remainingList); } // Unless of course the list is empty, in which case we can just return // the currentTotal value. else { return currentTotal; }}

Una advertencia: la recursividad hará que sus programas sean más legibles y es esencial para programar con un estilo funcional. Sin embargo, en algunos lenguajes (incluido JavaScript), tendrá problemas cuando su programa realice una gran cantidad de llamadas recursivas en una sola operación (al momento de escribir, "grande" es aproximadamente 10,000 llamadas en Chrome, 50,000 en Firefox y 11.000 en Node.js ). Los detalles están fuera del alcance de este artículo, pero lo esencial es que, al menos hasta que se lance ECMAScript 6, JavaScript no admite algo llamado "recursividad de cola", que es una forma más eficiente de recursividad. Este es un tema avanzado y no surgirá muy a menudo, pero vale la pena conocerlo.

 

Dejando eso de lado, recuerde que necesitábamos calcular la temperatura total a partir de una serie de temperaturas para luego calcular el promedio. Ahora, en lugar de recorrer cada elemento de la temperaturesmatriz, simplemente podemos escribir esto:

var totalTemp = totalForArray(0, temperatures);

Si eres purista, podrías decir que nuestra totalForArrayfunción podría desglosarse aún más. Por ejemplo, la tarea de sumar dos números probablemente aparecerá en otras partes de su aplicación y posteriormente debería ser su propia función.

function addNumbers(a, b) { return a + b;}

Ahora, nuestra totalForArrayfunción se ve así:

function totalForArray(currentTotal, arr) { currentTotal = addNumbers(currentTotal, arr[0]); var remainingArr = arr.slice(1); if(remainingArr.length 0) { return totalForArray(currentTotal, remainingArr); } else { return currentTotal; }}

¡Excelente! Devolver un valor único de una matriz es bastante común en la programación funcional, hasta el punto de que tiene un nombre especial, "reducción", que escucharás más comúnmente como un verbo, como cuando "reduces una matriz a un valor único". .” JavaScript tiene un método especial solo para realizar esta tarea común. Mozilla Developer Network proporciona una explicación completa , pero para nuestros propósitos es tan simple como esto:

// The reduce method takes a function as its first argument, and that function // accepts both the current item in the list and the current total result from // whatever calculation you're performing.var totalTemp = temperatures.reduce(function(previousValue, currentValue){ // After this calculation is returned, the next currentValue will be // previousValue + currentValue, and the next previousValue will be the // next item in the array. return previousValue + currentValue;});

Pero bueno, como ya hemos definido una addNumberfunción, podemos usarla en su lugar.

var totalTemp = temperatures.reduce(addNumbers);

De hecho, debido a que sumar una matriz es genial, pongámoslo en su propia función para que podamos usarlo nuevamente sin tener que recordar todas esas cosas confusas sobre la reducción y la recursividad.

function totalForArray(arr) { return arr.reduce(addNumbers);}var totalTemp = totalForArray(temperatures);

¡Ah, ese es un código legible! Para que lo sepas, métodos como los que reduceson comunes en la mayoría de los lenguajes de programación funcionales. Estos métodos auxiliares que realizan acciones en matrices en lugar de realizar bucles a menudo se denominan "funciones de orden superior".

 

Siguiendo adelante, la segunda tarea que enumeramos fue calcular un promedio. Esto es bastante fácil.

function average(total, count) { return total / count;}

¿Cómo podríamos obtener el promedio de una matriz completa?

function averageForArray(arr) { return average(totalForArray(arr), arr.length);}var averageTemp = averageForArray(temperatures);

Con suerte, estás empezando a ver cómo combinar funciones para realizar tareas más complejas. Esto es posible porque seguimos las reglas establecidas al principio de este artículo, es decir, que nuestras funciones siempre deben aceptar argumentos y devolver datos. Bastante impresionante.

Por último, queríamos recuperar una única propiedad de una serie de objetos. En lugar de mostrarte más ejemplos de recursividad, iré al grano y te daré pistas sobre otro método integrado de JavaScript: map . Este método es para cuando tienes una matriz con una estructura y necesitas asignarla a otra estructura, así:

// The map method takes a single argument, the current item in the list. Check// out the link above for more complete examples.var allTemperatures = data.map(function(item) { return item.temperatures;});

Eso es genial, pero extraer una sola propiedad de una colección de objetos es algo que harás todo el tiempo, así que creemos una función solo para eso. MX Motocross

// Pass in the name of the property that you'd like to retrievefunction getItem(propertyName) { // Return a function that retrieves that item, but don't execute the function. // We'll leave that up to the method that is taking action on items in our // array. return function(item) { return item[propertyName]; }}

Compruébelo: ¡Hemos creado una función que devuelve una función! Ahora podemos pasarlo al mapmétodo como este:

var temperatures = data.map(getItem('temperature'));

En caso de que le gusten los detalles, la razón por la que podemos hacer esto es porque, en JavaScript, las funciones son "objetos de primera clase", lo que básicamente significa que puede pasar funciones como cualquier otro valor. Si bien esta es una característica de muchos lenguajes de programación, es un requisito de cualquier lenguaje que pueda usarse con un estilo funcional. Por cierto, esta es también la razón por la que puedes hacer cosas como $(‘#my-element’).on(‘click’, function(e) … ). El segundo argumento del onmétodo es a functiony cuando pasas funciones como argumentos, las estás usando tal como usarías valores en lenguajes imperativos. Con buena pinta.

Finalmente, incluyamos la llamada mapen su propia función para hacer las cosas un poco más legibles.

function pluck(arr, propertyName) { return arr.map(getItem(propertyName));} var allTemperatures = pluck(data, 'temperatures');

Muy bien, ahora tenemos un conjunto de herramientas de funciones genéricas que podemos usar en cualquier lugar de nuestra aplicación, incluso en otros proyectos. Podemos contar los elementos de una matriz, obtener el valor promedio de una matriz y crear nuevas matrices extrayendo propiedades de listas de objetos. Por último, pero no menos importante, volvamos a nuestro problema original:

 

var data = [ { name: "Jamestown", population: 2047, temperatures: [-34, 67, 101, 87] }, …];

Necesitamos transformar una serie de objetos como el de arriba en una serie de x, ypares, como este:

[ [75, 1000000], …];

Aquí xestá la temperatura promedio y yla población total. Primero, aislamos los datos que necesitamos.

var populations = pluck(data, 'population');var allTemperatures = pluck(data, 'temperatures');

Ahora, hagamos una serie de promedios. Recuerde que la función que pasemos mapserá llamada en cada elemento de la matriz; entonces, el valor devuelto de esa función pasada se agregará a una nueva matriz, y esa nueva matriz finalmente se asignará a nuestra averageTempsvariable.

var averageTemps = allTemperatures.map(averageForArray);

Hasta ahora, todo bien. Pero ahora tenemos dos matrices:

// populations[2047, 3568, 1000000]// averageTemps[55.25, 5.5, 75]

Obviamente, solo queremos una matriz, así que escribamos una función para combinarlas. Nuestra función debe asegurarse de que el elemento en el índice 0de la primera matriz esté emparejado con el elemento en el índice 0de la segunda matriz, y así sucesivamente para los índices 1( ndonde nestá el número total de elementos en la matriz).

function combineArrays(arr1, arr2, finalArr) { // Just so we don't have to remember to pass an empty array as the third // argument when calling this function, we'll set a default. finalArr = finalArr || []; // Push the current element in each array into what we'll eventually return finalArr.push([arr1[0], arr2[0]]); var remainingArr1 = arr1.slice(1), remainingArr2 = arr2.slice(1); // If both arrays are empty, then we're done if(remainingArr1.length === 0 remainingArr2.length === 0) { return finalArr; } else { // Recursion! return combineArrays(remainingArr1, remainingArr2, finalArr); }};var processed = combineArrays(averageTemps, populations);

O, porque las frases ingeniosas son divertidas:

var processed = combineArrays(pluck(data, 'temperatures').map(averageForArray), pluck(data, 'population'));// [// [ 55.25, 2047 ],// [ 5.5, 3568 ],// [ 75, 1000000 ]// ]

Seamos realistas

Por último, pero no menos importante, veamos un ejemplo más del mundo real, esta vez agregando a nuestro cinturón de herramientas funcionales Underscore.js , una biblioteca de JavaScript que proporciona una serie de excelentes ayudas de programación funcionales. Extraeremos datos de una plataforma para información sobre conflictos y desastres en la que he estado trabajando llamada CrisisNET y usaremos la fantástica biblioteca D3 para visualizar esos datos.

El objetivo es brindar a las personas que visitan la página de inicio de CrisisNET una instantánea rápida de los tipos de información en el sistema. Para demostrar esto, podríamos contar la cantidad de documentos de la API que están asignados a una categoría particular, como “violencia física” o “conflicto armado”. De esta forma, el usuario puede ver cuánta información hay disponible sobre los temas que le parecen más interesantes.

 

Un gráfico de burbujas podría ser una buena opción, porque a menudo se utilizan para representar los tamaños relativos de grandes grupos de personas. Afortunadamente, D3 tiene una visualización incorporada nombrada packprecisamente para este propósito. Entonces, creemos un gráfico packque muestre la cantidad de veces que el nombre de una categoría determinada aparece en una respuesta de la API de CrisisNET.

Antes de continuar, tenga en cuenta que D3 es una biblioteca compleja que merece su propio tutorial (o muchos tutoriales, de hecho). Debido a que este artículo se centra en la programación funcional, no dedicaremos mucho tiempo a explicar cómo funciona D3. Pero no se preocupe: si aún no está familiarizado con la biblioteca, debería poder copiar y pegar los fragmentos de código específicos de D3 y profundizar en los detalles en otro momento. Los tutoriales D3 de Scott Murray son un gran recurso si está interesado en aprender más.

Continuando, primero asegurémonos de tener un elemento DOM, para que D3 tenga algún lugar para colocar el gráfico que generará con nuestros datos.

div/div

Ahora, creemos nuestro gráfico y agréguelo al DOM.

// width of chartvar diameter = 960, format = d3.format(",d"), // creates an ordinal scale with 20 colors. See D3 docs for hex values color = d3.scale.category20c(),// chart object to which we'll be adding datavar bubble = d3.layout.pack() .sort(null) .size([diameter, diameter]) .padding(1.5);// Add an SVG to the DOM that our pack object will use to draw the // visualization.var svg = d3.select("#bubble-graph").append("svg") .attr("width", diameter) .attr("height", diameter) .attr("class", "bubble");

El packobjeto toma una serie de objetos en este formato:

{ children: [ { className: , package: "cluster", value: } ]}

La API de datos de CrisisNET devuelve información en este formato:

{ data: [ { summary: "Example summary", content: "Example content", … tags: [ { name: "physical-violence", confidence: 1 } ] } ]}

Vemos que cada documento tiene una tagspropiedad y esa propiedad contiene una serie de elementos. Cada elemento de etiqueta tiene una namepropiedad, que es lo que buscamos. Necesitamos encontrar cada nombre de etiqueta único en la respuesta API de CrisisNET y contar la cantidad de veces que aparece ese nombre de etiqueta. Comencemos aislando la información que necesitamos usando la pluckfunción que creamos anteriormente.

var tagArrays = pluck(data, 'tags');

Esto nos da una serie de matrices, como esta:

[ [ { name: "physical-violence", confidence: 1 } ], [ { name: "conflict", confidence: 1 } ]]

Sin embargo, lo que realmente queremos es una matriz con todas las etiquetas que contenga. Entonces, usemos una función útil de Underscore.js llamada flatten . Esto tomará valores de cualquier matriz anidada y nos dará una matriz que tiene un nivel de profundidad.

 

var tags = _.flatten(tagArrays);

Ahora, nuestra matriz es un poco más fácil de manejar:

[ { name: "physical-violence", confidence: 1 }, { name: "conflict", confidence: 1 }]

Podemos usarlo plucknuevamente para obtener lo que realmente queremos, que es una lista simple de solo los nombres de las etiquetas.

var tagNames = pluck(tags, 'name');[ "physical-violence", "conflict"]

Ah, eso es mejor.

Ahora nos toca la tarea relativamente sencilla de contar el número de veces que aparece cada nombre de etiqueta en nuestra lista y luego transformar esa lista en la estructura requerida por el packdiseño D3 que creamos anteriormente. Como probablemente habrás notado, las matrices son una estructura de datos bastante popular en la programación funcional; la mayoría de las herramientas están diseñadas teniendo en cuenta las matrices. Entonces, como primer paso, crearemos una matriz como esta:

[ [ "physical-violence", 10 ], [ "conflict", 27 ]]

Aquí, cada elemento de la matriz tiene el nombre de la etiqueta en el índice 0y el recuento total de esa etiqueta en el índice 1. Solo queremos una matriz para cada nombre de etiqueta único, así que comencemos creando una matriz en la que cada nombre de etiqueta aparezca solo una vez. Afortunadamente, existe un método Underscore.js solo para este propósito.

var tagNamesUnique = _.uniq(tagNames);

También eliminemos cualquier valor false-y( false,,, etc.) usando otra útil función de Underscore.js null.””

tagNamesUnique = _.compact(tagNamesUnique);

Desde aquí, podemos escribir una función que genere nuestras matrices usando otro método de colección integrado de JavaScript, llamado filtro , que filtra una matriz según una condición.

function makeArrayCount(keys, arr) { // for each of the unique tagNames return keys.map(function(key) { return [ key, // Find all the elements in the full list of tag names that match this key // and count the size of the returned array. arr.filter(function(item) { return item === key; }).length ] });}

Ahora podemos crear fácilmente la estructura de datos que packnecesitamos mapeando nuestra lista de matrices.

var packData = makeArrayCount(tagNamesUnique, tagNames).map(function(tagArray) { return { className: tagArray[0], package: "cluster", value: tagArray[1] }});

Finalmente, podemos pasar nuestros datos a D3 y generar nodos DOM en nuestro SVG, un círculo para cada nombre de etiqueta único, con un tamaño relativo al número total de veces que ese nombre de etiqueta apareció en la respuesta API de CrisisNET.

function setGraphData(data) { var node = svg.selectAll(".node") // Here's where we pass our data to the pack object. .data(bubble.nodes(data) .filter(function(d) { return !d.children; })) .enter().append("g") .attr("class", "node") .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; }); // Append a circle for each tag name. node.append("circle") .attr("r", function(d) { return d.r; }) .style("fill", function(d) { return color(d.className); }); // Add a label to each circle, using the tag name as the label's text node.append("text") .attr("dy", ".3em") .style("text-anchor", "middle") .style("font-size", "10px") .text(function(d) { return d.className } ); }

Poniéndolo todo junto, aquí están las funciones setGraphDatay makeArrayen contexto, incluida una llamada a la API de CrisisNET usando jQuery (necesitará obtener una clave API). También publiqué un ejemplo completamente funcional en GitHub .

function processData(dataResponse) { var tagNames = pluck(_.flatten(pluck(dataResponse.data, 'tags')), 'name'); var tagNamesUnique = _.uniq(tagNames); var packData = makeArrayCount(tagNamesUnique, tagNames).map(function(tagArray) { return { className: tagArray[0], package: "cluster", value: tagArray[1] } }); return packData;}function updateGraph(dataResponse) { setGraphData(processData(dataResponse));}var apikey = // Get an API key here: https://api.crisis.netvar dataRequest = $.get('https://api.crisis.net/item?limit=100apikey=' + apikey);dataRequest.done( updateGraph );

Esa fue una inmersión bastante profunda, así que ¡felicidades por seguir adelante! Como mencioné, estos conceptos pueden ser desafiantes al principio, pero resista la tentación de crear forbucles por el resto de su vida.

A las pocas semanas de utilizar técnicas de programación funcional, creará rápidamente un conjunto de funciones simples y reutilizables que mejorarán drásticamente la legibilidad de sus aplicaciones. Además, podrá manipular estructuras de datos mucho más rápidamente, eliminando lo que solían ser 30 minutos de frustrante depuración en un par de líneas de código. Una vez que sus datos hayan sido formateados correctamente, podrá dedicar más tiempo a la parte divertida: ¡hacer que la visualización se vea increíble!

Otras lecturas

  • Una introducción a los sistemas de tipos de programación
  • Una introducción a Redux
  • Una introducción a JavaScript de pila completa
  • Programación declarativa y la web

(al, il, mrn)Explora más en

  • Codificación
  • javascript
  • Técnicas





Tal vez te puede interesar:

  1. ¿Deberían abrirse los enlaces en ventanas nuevas?
  2. 24 excelentes tutoriales de AJAX
  3. 70 técnicas nuevas y útiles de AJAX y JavaScript
  4. Más de 45 excelentes recursos y repositorios de fragmentos de código

No tengas miedo de la programación funcional

No tengas miedo de la programación funcional

Índice La programación funcional puede resultar abrumadora

programar

es

https://pseint.es/static/images/programar-no-tengas-miedo-de-la-programacion-funcional-851-0.jpg

2024-04-04

 

No tengas miedo de la programación funcional
No tengas miedo de la programación funcional

Si crees que alguno de los contenidos (texto, imagenes o multimedia) en esta página infringe tus derechos relativos a propiedad intelectual, marcas registradas o cualquier otro de tus derechos, por favor ponte en contacto con nosotros en el mail [email protected] y retiraremos este contenido inmediatamente

 

 

Top 20