diff --git a/README.es.md b/README.es.md new file mode 100644 index 0000000..7bb75e2 --- /dev/null +++ b/README.es.md @@ -0,0 +1,113 @@ +# Taller de Javascript Funcional + +[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/nodeschool/discussions?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +Discusiones de NodeSchool + +### Enseñando las propiedades fundamentales de Javascript Funcional. + +#### No se requieren librerías (e.j. sin `underscore`), tan solo ES5. + +
+ +
+ +[![Gittip](http://img.shields.io/gittip/timoxley.png)](https://www.gittip.com/timoxley/) + +## Misión + +Muchos recursos de aprendizaje de programación funcional te enseñarán como escribir código al estilo funcional, pero usualmente es altamente indirecto, profundamente abstraído, requiere el entendimiento de complejas relaciones entre llamados de librerías personalizadas y no representa la realidad de como la gente escribe realmente JavaScript. + +La meta de este taller es de crear problemas realistas que pueden ser resueltos usando JavaScript nativo y conciso. + +**Por favor lee los ejercicios cuidadosamente y sigue todas las instrucciones, están para ayudarte a aprender** + +## Instalación y actualización + +``` +$ npm install -g functional-javascript-workshop@latest +``` + +Algunas instalaciones de npm requieren el uso de `sudo` en el comando de arriba. Es recomendado en vez [re-instalar node/npm para que no necesites usar sudo](https://gist.github.com/isaacs/579814). + +## Instrucciones de uso + +#### 1. Elige el problema en el que quieras trabajar + +Una vez el taller está instalado, corre `functional-javascript-workshop` para imprimir en consola un menú donde puedas seleccionar un problema para trabajar. + +``` +$ functional-javascript-workshop +``` + +Los problemas están listados en un vago orden de dificultad. Se recomienda completarlos en orden, debido a que ejercicios posteriores serán resueltos por habilidades desarrolladas al resolver problemas previos. + +#### 2. Escribiendo tu solución + +Una vez has elegido un problema, el taller recordará en cual problema estás trabajando. Usando tu editor preferido, simplemente crea un archivo en el cual escribir tu solución. La mayoría de problemas proveerán algúna plantilla con la cual comenzar. Cópialo de la descripción del problema a tu archivo de solución. + +#### 3. Probando tu solución + +Usa el comando del taller `run` para apuntar el taller a tu archivo de solución. Tu solución será cargada y pasada a el proceso del problema. Esto usualmente no correrá ningún tipo de validación, simplemente mostrará el resultado del programa. + +``` +$ functional-javascript-workshop run misolucion.js +``` + +#### 4. Verificando tu solución + +Tu solución será verificada contra el resultado de la solución 'oficial'. Si todos los resultados concuerdan, ¡has solucionado el problema correctamente!. + +``` +$ functional-javascript-workshop verify misolucion.js +``` + +## ¿Problemas? + +Retroalimentación y recomendaciones son recibidas, por favor informa de tus problemas en [issues](https://github.com/timoxley/functional-javascript-workshop/issues). + +Reseñas completas [como esta](https://github.com/timoxley/functional-javascript-workshop/issues/7) son increíblemente útiles. ¡Más reseñas así por favor!. + +Estamos a la búsqueda de más problemas prácticos, así que si tienes un problema en tu día a día el cual fue solucionado simple y elegantemente con alguna técnica de JavaScript funcional, dejanos saber para crear un ejercicio en base a esta. + +## Pantallazos + +![screen shot 2013-09-27 at 5 18 45 pm](https://f.cloud.github.com/assets/43438/1225514/08c87a70-276a-11e3-8db7-485e3c760373.png) +![screen shot 2013-09-23 at 9 13 02 pm](https://f.cloud.github.com/assets/43438/1191466/f289f38a-2451-11e3-9ba5-a3c224b5ca97.png) + +## Recursos + +[Una colección en crecimiento de recursos sobre JavaScript funcional puede ser encontrada en la wiki](https://github.com/timoxley/functional-javascript-workshop/wiki). + +## Gracias rvagg + +Este tutorial fue construido usando el [framework de talleres](https://github.com/rvagg/workshopper) de rvagg. + +## Resumen + +``` + proyecto : functional-javascript + edad del repositorio : 2 años + activo : 50 días + commits : 152 + archivos : 70 + autores : + 69 Tim 45.4% + 67 Tim Oxley 44.1% + 3 Sequoia McDowell 2.0% + 2 ZJ 1.3% + 1 Naor Biton 0.7% + 1 Pavel Kornilov 0.7% + 1 Steve Teo 0.7% + 1 Wei Lu 0.7% + 1 Yoshua Wuyts 0.7% + 1 flakyfilibuster 0.7% + 1 Arvid Andersson 0.7% + 1 tim walker 0.7% + 1 Brendon Murphy 0.7% + 1 Lorcan Coyle 0.7% + 1 Matthew Hokanson 0.7% +``` + +## Licencia + +MIT diff --git a/Readme.md b/README.md similarity index 100% rename from Readme.md rename to README.md diff --git a/exercises/async_loops/problem.es.md b/exercises/async_loops/problem.es.md new file mode 100644 index 0000000..5ca375f --- /dev/null +++ b/exercises/async_loops/problem.es.md @@ -0,0 +1,51 @@ +¡El código está roto! + +¡Un desarrollador Java ha subido su terrible código a nuestro proyecto y no lo probó! + +```js +function loadUsers(userIds, load, done) { + var users = []; + for (var i = 0; i < userIds.length; i++) { + users.push(load(userIds[i])); + } + return users; +} + +module.exports = loadUsers; +``` + +# Tarea + +¡Arregla este código! El callback debería ser llamado con todos los usuarios cargados. El orden de los usuarios debería concordar el order de los IDs cargados. Debido a que esta función es asíncrona, no nos importa el valor de retorno. + +## Parámetros + +- `userIds`: Un `Array` de IDs de usuario numéricos. +- `load`: Una función usada para cargar un objeto de tipo usuario. Recibe un ID numérico y un callback. El callback será llamado con el resultado de cargar el usuario con el ID provisto (puede ser un objeto usuario o `null`). +- `done`: Una función que recibe un Array de objetos usuario (como los devueltos en `load`). + +## Condiciones + +- No uses ciclos `for/while` (Array#forEach está bien). +- El orden de los resultados en `done` debe ser el mismo en el fueron especificados en `userIds`. +- Los usuarios deberían ser cargado es paralelo, e.j. todo el proceso no debería tomar más de un segundo. +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Pista + +- No necesitas organizar para mantener el orden. +- Usar `console.log` afectará la verificación. Solo usa `console.log` cuando corras `functional-javascript run`. + +## Plantilla + +```js +function loadUsers(userIds, load, done) { + var users = []; + for (var i = 0; i < userIds.length; i++) { + users.push(load(userIds[i])); + } + return users; +} + +module.exports = loadUsers; +``` diff --git a/exercises/basic_call/problem.es.md b/exercises/basic_call/problem.es.md new file mode 100644 index 0000000..c9558e4 --- /dev/null +++ b/exercises/basic_call/problem.es.md @@ -0,0 +1,105 @@ +JavaScript permite "duck typing", el cual es una forma de tipeado dinámico en el cual las propiedades y métodos de un objeto determinan la validez semántica, en vez de su herencia de una clase en particular o implementación de un interfaz en particular. El nombre del concepto se refiere al "duck test" (prueba de pato), atribuida a James Whitcomb Riley, el cual puede ser formulado así: + +> Cuando veo un ave que camina como un pato, nada como un pato y grazna como un pato, digo que esa ave es un pato. + +En JavaScript, para escribir programas robustos necesitamos revisar que un objeto es del tipo que necesitamos. + +Podemos usar `Object#hasOwnProperty` para saber si un objeto "tiene" una propiedad definida en si mismo (e.j. no heredada desde su prototipo): + +```js +var duck = { + quack: function() { + console.log("quack"); + } +}; + +duck.hasOwnProperty("quack"); // => true +``` + +Nosotros no le dimos a `duck` el método #hasOwnProperty, ¿de donde vino?. + +`duck` fue creado con la sintaxis de `{}`, y esta hereda de `Object.prototype`: + +```js +var object = { quack: true }; + +Object.getPrototypeOf(object) === Object.prototype; // => true +object.hasOwnProperty("quack"); // => true +``` + +¿Pero qué pasa si un objeto no hereda de `Object.prototype`?. + +```js +// crea un objeto con el prototipo de `null` +var object = Object.create(null); +object.quack = function() { + console.log("quack"); +}; + +Object.getPrototypeOf(object) === Object.prototype; // => false +Object.getPrototypeOf(object) === null; // => true + +object.hasOwnProperty("quack"); +// => TypeError: Object object has no method 'hasOwnProperty' +``` + +Aún podemos usar `hasOwnProperty` de `Object.prototype`, si lo llamamos con el valor de `this` con "algo similar a un objeto". `Function#call` nos permite llamar cualquiera función con valor de `this` alterado. + +```js +// el primer argumento se convierte en el valor de `this` +// el resto de los argumentos son pasados a la función + +Object.prototype.hasOwnProperty.call(object, "quack"); // => true +``` + +# Tarea: + +Escribe una función `duckCount` que retorne el número de argumentos provistos que contienen una propiedad "quack" definida en ellos. No cuentes valores heredados de los prototipos. + +Ejemplo: + +```js +var notDuck = Object.create({ quack: true }); +var duck = { quack: true }; +duckCount(duck, notDuck); // 1 +``` + +## Argumentos + +- Se te proveerán de 0 a 20 argumentos. Cada argumento podría ser de cualquier tipo con cualquier propiedad. Algunos de estos tendrán una propiedad "quack". + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. +- No crees ninguna variable como contador/acumulador. +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Pista + +- La variable `arguments`, disponible en todas las funciones, es un _Object_ similar a un _Array_ de esta manera: + +```js +{ + 0: 'argument0', + 1: 'argument1', // etc + length: 2 +} +``` + +## Recursos + +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice#Array-like +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/arguments + +## Plantilla + +```js +function duckCount() { + // TU SOLUCIÓN +} + +module.exports = duckCount; +``` diff --git a/exercises/basic_every_some/problem.es.md b/exercises/basic_every_some/problem.es.md new file mode 100644 index 0000000..48beb36 --- /dev/null +++ b/exercises/basic_every_some/problem.es.md @@ -0,0 +1,48 @@ +# Tarea + +Retorna una función que tome una lista de usuarios validos, y retorna una función que retorne verdadero si todos los usuarios existen en la lista original de usuarios. + +Solo necesitas revisar que los IDs concuerden. + +## Ejemplo + +```js +var goodUsers = [{ id: 1 }, { id: 2 }, { id: 3 }]; + +// `checkUsersValid` es la función que definirás +var testAllValid = checkUsersValid(goodUsers); + +testAllValid([{ id: 2 }, { id: 1 }]); +// => true + +testAllValid([{ id: 2 }, { id: 4 }, { id: 1 }]); +// => false +``` + +## Argumentos + +- `goodUsers`: Una lista de usuarios validos. + +Usa `array#some` y `Array#every` para verificar que cada usuario pasado a tu función retornada existe en en el array pasado a la función exportada. + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Recursos + +- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/every +- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/some + +## Plantilla + +```js +function checkUsersValid(goodUsers) { + return function allUsersValid(submittedUsers) { + // TU SOLUCIÓN + }; +} + +module.exports = checkUsersValid; +``` diff --git a/exercises/basic_filter/problem.es.md b/exercises/basic_filter/problem.es.md new file mode 100644 index 0000000..60ed218 --- /dev/null +++ b/exercises/basic_filter/problem.es.md @@ -0,0 +1,50 @@ +# Tarea + +Usa `Array#filter` para escribir una función llamada `getShortMessages`. + +`getShortMessages` recibe un `array` de objetos con la propiedad `message` y retorna un `array` de mensajes que _tienen menos de 50 caracteres_. + +La función debería retornar un `array` conteniendo los mensajes _sin su objeto contenedor_. + +## Argumentos + +- mensajes: un `Array` de 10 a 100 objetos aleatorios que se ven algo así: + +```js +{ + message: "Esse id amet quis eu esse aute officia ipsum."; // aleatorio +} +``` + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Pista + +- ¡Trata de encadenar algunos métodos de `Array`! + +## Ejemplo + +``` +[ 'Tempor quis esse consequat sunt ea eiusmod.', + 'Id culpa ad proident ad nulla laborum incididunt.', + 'Ullamco in ea et ad anim anim ullamco est.', + 'Est ut irure irure nisi.' ] +``` + +## Recursos + +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map + +## Plantilla + +```js +function getShortMessages(messages) { + // TU SOLUCIÓN +} + +module.exports = getShortMessages; +``` diff --git a/exercises/basic_inheritance_with_objectcreate/problem.es.md b/exercises/basic_inheritance_with_objectcreate/problem.es.md new file mode 100644 index 0000000..1d3fde9 --- /dev/null +++ b/exercises/basic_inheritance_with_objectcreate/problem.es.md @@ -0,0 +1,74 @@ +# Tarea + +Crea un nuevo tipo "BetterUser" que extienda "User" y sobreescribe el método `.toString` de "User". + +La función que exportes será pasada a la función del constructor de "User" que se verá así: + +```js +/** + * Constructor de User. + * + * @param title {String} Prefijo para el Usuario. e.j: "Sr", "Sra", "Srta", etc. + * @param name {String} Nombre del Usuario. e.g: 'Pepito Peréz'. + */ + +function User(title, name) { + this.title = title; + this.name = name; + console.info("NEW USER: " + this); +} + +/** + * Crea un nombre completo para un usuario. + * @return {String} Nombre a mostrar + */ + +User.prototype.displayName = function() { + return this.title + " " + this.name; +}; + +/** + * @return {String} Prefijo y nombre formateado. + */ + +User.prototype.toString = function() { + return "[User:" + this.displayName() + "]"; +}; +``` + +Notas: No necesitas copiar esto en tu solución. + +## Ejemplo + +De tu función exportada, retorna un función constructora `BetterUser` que extienda `User` con un método personalizado `toString` que funcione de esta manera: + +```js +var joe = new BetterUser("Sr.", "Elver Mires"); // pasa prefijo y nombre +console.log("Hola " + joe); // 'Hola [BetterUser: Sr. Elver Mires]' +``` + +## Condiciones + +- ¡No llames el constructor de `User` innecesariamente! +- No uses `__proto__` +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Recursos + +- http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/ +- http://tobyho.com/2011/11/11/js-object-inheritance/ +- http://hughfdjackson.com/javascript/2012/01/05/prototypes:-the-short%28est-possible%29-story/ + +## Plantilla + +```js +// User es un constructor +function upgradeUser(User) { + // EDITA ESTO CUANTO NECESITES + function BetterUser() {} + + return BetterUser; +} + +module.exports = upgradeUser; +``` diff --git a/exercises/basic_inheritance_without_objectcreate/problem.es.md b/exercises/basic_inheritance_without_objectcreate/problem.es.md new file mode 100644 index 0000000..fcb721b --- /dev/null +++ b/exercises/basic_inheritance_without_objectcreate/problem.es.md @@ -0,0 +1,85 @@ +# Tarea + +Haz la misma tarea que en "Basic Inhertance with Object.create", pero no uses `Object.create`! + +Los detalles están abajo para tu referencia. + +## Condiciones + +- ¡No llames el constructor de `User` innecesariamente!. +- No uses `Object.create`. +- No uses `__proto__`. + +## Pista + +- Los prototipos siempre son objetos. +- Tus instancias de `BetterUser` necesitan "heredar" de `User.prototype` +- No hay razon por la que no puedas crear objetos de prueba en arbol de herencia. +- Entiende que es lo que `Object.create` hace. + +## Recursos + +- http://www.bennadel.com/blog/2184-Object-create-Improves-Constructor-Based-Inheritance-In-Javascript-It-Doesn-t-Replace-It.htm + +## Definición de tarea previa + +Crea un nuevo tipo "BetterUser" que extienda "User" y sobreescribe el método `.toString` de "User". + +La función que exportes será pasada a la función del constructor de "User" que se verá así: + +```js +/** + * Constructor de User. + * + * @param title {String} Prefijo para el Usuario. e.j: "Sr", "Sra", "Srta", etc. + * @param name {String} Nombre del Usuario. e.g: 'Pepito Peréz'. + */ + +function User(title, name) { + this.title = title; + this.name = name; + console.info("NEW USER: " + this); +} + +/** + * Crea un nombre completo para un usuario. + * @return {String} Nombre a mostrar + */ + +User.prototype.displayName = function() { + return this.title + " " + this.name; +}; + +/** + * @return {String} Prefijo y nombre formateado. + */ + +User.prototype.toString = function() { + return "[User:" + this.displayName() + "]"; +}; +``` + +Notas: No necesitas copiar esto en tu solución. + +## Ejemplo + +De tu función exportada, retorna un función constructora `BetterUser` que extienda `User` con un método personalizado `toString` que funcione de esta manera: + +```js +var joe = new BetterUser("Sr.", "Elver Mires"); // pasa prefijo y nombre +console.log("Hola " + joe); // 'Hola [BetterUser: Sr. Elver Mires]' +``` + +## Plantilla + +```js +// User es un constructor +function upgradeUser(User) { + // EDITA ESTO CUANTO NECESITES + function BetterUser() {} + + return BetterUser; +} + +module.exports = upgradeUser; +``` diff --git a/exercises/basic_inheritance_without_objectcreate/problem.md b/exercises/basic_inheritance_without_objectcreate/problem.md index 40c8ca0..1e31247 100644 --- a/exercises/basic_inheritance_without_objectcreate/problem.md +++ b/exercises/basic_inheritance_without_objectcreate/problem.md @@ -1,25 +1,25 @@ # Task -Do the exact same task as "Basic Inhertance with Object.create", except don't use Object.create! +Do the exact same task as "Basic Inheritance with Object.create", except don't use Object.create! Details copied below for your reference. ## Conditions -* Don't call the User constructor unnecessarily! -* Don't use `Object.create`. -* Don't use `__proto__` +- Don't call the User constructor unnecessarily! +- Don't use `Object.create`. +- Don't use `__proto__` ## Hints -* Prototypes are always Objects -* Your `BetterUser` instances need to 'inherit' from `User.prototype` -* No reason you can't create dummy objects in the inheritance tree -* Understand what `Object.create` does. +- Prototypes are always Objects +- Your `BetterUser` instances need to 'inherit' from `User.prototype` +- No reason you can't create dummy objects in the inheritance tree +- Understand what `Object.create` does. ## Resources -* http://www.bennadel.com/blog/2184-Object-create-Improves-Constructor-Based-Inheritance-In-Javascript-It-Doesn-t-Replace-It.htm +- http://www.bennadel.com/blog/2184-Object-create-Improves-Constructor-Based-Inheritance-In-Javascript-It-Doesn-t-Replace-It.htm ## Previous Task Definition @@ -36,9 +36,9 @@ Your exported function will be passed the constructor function for a "User" type */ function User(title, name) { - this.title = title - this.name = name - console.info('NEW USER: ' + this) + this.title = title; + this.name = name; + console.info("NEW USER: " + this); } /** @@ -47,16 +47,16 @@ function User(title, name) { */ User.prototype.displayName = function() { - return this.title + ' ' + this.name -} + return this.title + " " + this.name; +}; /** * @return {String} Formatted name & title */ User.prototype.toString = function() { - return '[User: '+this.displayName()+']' -} + return "[User: " + this.displayName() + "]"; +}; ``` Note: you do not need to copy this into your solution. @@ -66,8 +66,8 @@ Note: you do not need to copy this into your solution. From your exported function, return a `BetterUser` constructor function that extends `User` with a custom `toString` method that works like so: ```js -var joe = new BetterUser('Mr.', 'Joe Smith') // pass in title and name -console.log('Hello ' + joe) // 'Hello [BetterUser: Mr. Joe Smith]' +var joe = new BetterUser("Mr.", "Joe Smith"); // pass in title and name +console.log("Hello " + joe); // 'Hello [BetterUser: Mr. Joe Smith]' ``` ## Boilerplate @@ -75,14 +75,11 @@ console.log('Hello ' + joe) // 'Hello [BetterUser: Mr. Joe Smith]' ```js // User is a constructor function upgradeUser(User) { - // EDIT THESE AS NECESSARY - function BetterUser() { - - } + function BetterUser() {} - return BetterUser + return BetterUser; } -module.exports = upgradeuser +module.exports = upgradeuser; ``` diff --git a/exercises/basic_map/problem.es.md b/exercises/basic_map/problem.es.md new file mode 100644 index 0000000..b721230 --- /dev/null +++ b/exercises/basic_map/problem.es.md @@ -0,0 +1,39 @@ +# Tarea + +Convierte el siguiente código de un ciclo `for` a un `Array#map`: + +```js +function doubleAll(numbers) { + var result = []; + for (var i = 0; i < numbers.length; i++) { + result.push(numbers[i] * 2); + } + return result; +} + +module.exports = doubleAll; +``` + +## Argumentos + +- `numbers`: Un `Array` de 0 a 20 enteros entre 0 y 9. + +## Condiciones + +- Tu solución debería user `Array.prototype.map()` +- No uses ciclos `for/while` o `Array#forEach`. +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Recursos + +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map + +## Plantilla + +```js +function doubleAll(numbers) { + // TU SOLUCIÓN +} + +module.exports = doubleAll; +``` diff --git a/exercises/basic_recursion/problem.es.md b/exercises/basic_recursion/problem.es.md new file mode 100644 index 0000000..6a71bb6 --- /dev/null +++ b/exercises/basic_recursion/problem.es.md @@ -0,0 +1,68 @@ +La recursión es un concepto de programación fundamental el cual puede llevar a soluciones elegantes y eficientes a problemas algorítmicos. De hecho, la recursion es tan poderosa, que todo el comportamiento de iteración puede ser definido usando funciones recursivas. Encontrarás que la recursión es indispensable cuando se itera sobre estructuras de datos anidados. + +Una función recursiva es una función la cual se llama a sí misma. Por ejemplo, esta función recursiva tomará un `array` de palabras, y retornara un `array` de esas palabras, en mayúscula. + +```js +function toUpperArray(items) { + if (!items.length) return []; // condición final + var head = items[0]; // elemento con el que trabajar + head = head.toUpperCase(); // ejecuta acción + var tail = items.slice(1); // siguiente + return [head].concat(toUpperArray(tail)); // paso recursivo +} + +toUpperArray(["hello", "world"]); // => ['HELLO', 'WORLD'] +``` + +El punto de este ejercicio es familiarizarte con la recursion al implementar una interfaz familiar usando funciones recursivas. + +# Tarea + +Implementa `Array#reduce` usando recursión. + +Para probar que tu recursión funciona correctamente, usaremos tu implementación de `reduce` para ejecutar nuestra solución al previo problema de `basic_reduce`. e.j: tu función de `reduce` se le pasará un `array` de palabras, y una función, y el valor inicial el cual retornará un objeto conteniendo el contado por cada palabra encontrada en el `array`. No necesitas implementar esta función, se te proveerá a tu implementación de `reduce`. + +Para simplificar, tu implementación de `reduce` **debe replicar el comportamiento de un reductor sin un valor inicial**. Puedes asumir que el valor inicial siempré será provisto. + +## Argumentos + +- `arr`: Un `Array` el cual reducir. +- `fn`: Function para usar en el paso de reducción. Al igual que `Array#reduce`, esta función debe ser pasada `valorPrevio`, `valorActual`, `indice` y el `array` que estamos iterando. +- `init`: Valor inicial de la reducción. A diferencia de `Array#reduce`, este valor es requerido (puedes asumir que siempre te será provisto). + +## Ejemplo + +```js +// Tu función de reduce se debería comportar al +// igual que `Array#reduce`, pero tomará el `array` +// para operar como el primer argumento: + +reduce( + [1, 2, 3], + function(prev, curr, index, arr) { + return prev + curr; + }, + 0 +); +// => 6 +``` + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. +- No uses ningún método de `Array` como `Array#map` o `Array#reduce` + +## Recursos + +- https://en.wikipedia.org/wiki/Recursion +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce + +## Plantilla + +```js +function reduce(arr, fn, initial) { + // TU SOLUCIÓN +} + +module.exports = reduce; +``` diff --git a/exercises/basic_recursion/solution_es/solution.js b/exercises/basic_recursion/solution_es/solution.js new file mode 100644 index 0000000..00d2466 --- /dev/null +++ b/exercises/basic_recursion/solution_es/solution.js @@ -0,0 +1,8 @@ +function reduce(arr, fn, initial) { + return (function reduceOne(index, value) { + if (index > arr.length - 1) return value // condición final + return reduceOne(index + 1, fn(value, arr[index], index, arr)) // calcula y pasa los valores al siguiente paso + })(0, initial) // IIFE. dispara la recursión con los valores iniciales +} + +module.exports = reduce diff --git a/exercises/basic_reduce/problem.es.md b/exercises/basic_reduce/problem.es.md new file mode 100644 index 0000000..aa41e35 --- /dev/null +++ b/exercises/basic_reduce/problem.es.md @@ -0,0 +1,42 @@ +# Tarea + +Dado un `Array` de `strings`, usa `Array#reduce` para crear un objeto que contenga el número de veces que cada `string` se encuentra en el `array`. Devuelve el objeto directamente (no hay necesidad de usar `console.log`). + +## Ejemplo + +```js +var inputWords = ["Apple", "Banana", "Apple", "Durian", "Durian", "Durian"]; + +console.log(countWords(inputWords)); + +// => +// { +// Apple: 2, +// Banana: 1, +// Durian: 3 +// } +``` + +## Argumentos + +- `inputWords`: Un `array` de `strings` aleatorios. + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Recursos + +- https://en.wikipedia.org/wiki/Reduce_(higher-order_function) +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce + +## Plantilla + +```js +function countWords(inputWords) { + // TU SOLUCIÓN +} + +module.exports = countWords; +``` diff --git a/exercises/basic_reduce/solucion_es/solution.js b/exercises/basic_reduce/solucion_es/solution.js new file mode 100644 index 0000000..62015e4 --- /dev/null +++ b/exercises/basic_reduce/solucion_es/solution.js @@ -0,0 +1,8 @@ +function countWords(arr) { + return arr.reduce(function(countMap, word) { + countMap[word] = ++countMap[word] || 1; // incrementa o lo inicia en 1 + return countMap; + }, {}); // segundo argumento a reducir e inicia el countMap en `{}` +} + +module.exports = countWords; diff --git a/exercises/blocking_event_loop/problem.es.md b/exercises/blocking_event_loop/problem.es.md new file mode 100644 index 0000000..129e67a --- /dev/null +++ b/exercises/blocking_event_loop/problem.es.md @@ -0,0 +1,33 @@ +# Tarea + +Modifica la función recursiva `repeat` provista en la plantilla, de forma que no bloquea el ciclo del evento (e.j. `Timers` y manejadores de `IO` que se puedan disparar). Esto necesariamente requiere que `repeat` sea asíncrono. + +Un `timeout` es puesto en cola luego de 100 milisegundo, el cual imprimirá los resultados de la prueba y saldrá del proceso. `repeat` debería liberar el control del ciclo del evento para permitir que el `timeout` interrupta antes de que todas las operaciones se completen. + +Trata de realizar tantas operaciones como puedas antes de que el `timeout` se dispare. + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Pista + +- Si tu programa tarda en correr, algo está probablemente mal. Usa `Ctrl+C` para matar el proceso de `node`. + +## Recursos + +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Timers + +## Plantilla + +```js +function repeat(operation, num) { + // modifica esto para que pueda ser interrumpido + if (num <= 0) return; + operation(); + return repeat(operation, --num); +} + +module.exports = repeat; +``` diff --git a/exercises/blocking_event_loop/solution_es/solution.js b/exercises/blocking_event_loop/solution_es/solution.js new file mode 100644 index 0000000..5f4b19a --- /dev/null +++ b/exercises/blocking_event_loop/solution_es/solution.js @@ -0,0 +1,18 @@ +function repeat(operation, num) { + if (num <= 0) return; + + operation(); + + // libera el control cada 10 + // iteraciones o similar + // 10 es aleatorio. + if (num % 10 === 0) { + setTimeout(function() { + repeat(operation, --num); + }); + } else { + repeat(operation, --num); + } +} + +module.exports = repeat; diff --git a/exercises/blocking_event_loop/wrapper_es.js b/exercises/blocking_event_loop/wrapper_es.js new file mode 100644 index 0000000..efeb869 --- /dev/null +++ b/exercises/blocking_event_loop/wrapper_es.js @@ -0,0 +1,49 @@ +"use strict"; + +var path = require("path"); + +var repeat = require(path.resolve(process.cwd(), process.argv[2])); + +var count = 0; +var CYCLES = 10000; + +function operation() { + for (var i = 0; i < 1000000; i++) { + // quema algunos ciclos de la CPU + } + // cuenta cuantas veces ha sido llamada esta función + count++; +} + +console.error(); +console.error(operation.toString()); +console.error(); +console.error( + "Tratando de ejecutor las operaciones de arriba %d veces, puede que se congele...", + CYCLES +); +console.error(); +console.error("Presiona Ctrl+C para terminar el proceso."); +console.error(); + +var start = Date.now(); +repeat(operation, CYCLES); + +setTimeout(function() { + var end = Date.now(); + console.error("Se ejecutarón %d operaciones.", count); + + if (count < 10) { + console.log("¡Error! Debiste haber completado algunas operaciones"); + process.exit(1); + } + + if (count >= CYCLES) { + console.log("¡Error! No debiste haber completado todas las operaciones"); + process.exit(1); + } + + console.log("¡Las operaciones se interrumpieron exitosamente!"); + console.error("Interrumpido luego de %d milisegundos.", end - start); + process.exit(); +}, 100); diff --git a/exercises/currying/problem.es.md b/exercises/currying/problem.es.md new file mode 100644 index 0000000..07c94a8 --- /dev/null +++ b/exercises/currying/problem.es.md @@ -0,0 +1,74 @@ +Este es un ejemplo de la implementación de `curry3`, el cual hace "curry" de hasta 3 argumentos: + +```js +function curry3(fun) { + return function(one) { + return function(two) { + return function(three) { + return fun(one, two, three); + }; + }; + }; +} +``` + +Si fuésemos a utilizar está implementación con una función de prueba: + +```js +function abc(one, two, three) { + return one / two / three; +} +``` + +Funcionaría así: + +```js +var curryC = curry3(abc); +var curryB = curryC(6); +var curryA = curryB(3); + +console.log(curryA(2)); // => 1 +``` + +# Tarea + +En este reto, vamos a implementar una función de "curry" para un número arbitrario de argumentos. + +`curryN` tomará dos parámetros: + +- `fn`: La función que queremos convertir en "curry". +- `n`: Un número opcional de argumentos que se van a convertir en "curry". Si no se provee `curryN` debería usar el número de argumento de `fn` como valor de `n`. + +## Ejemplo + +```js +function add3(one, two, three) { + return one + two + three; +} + +var curryC = curryN(add3); +var curryB = curryC(1); +var curryA = curryB(2); +console.log(curryA(3)); // => 6 +console.log(curryA(10)); // => 13 + +console.log(curryN(add3)(1)(2)(3)); // => 6 +``` + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. + +## Pista + +- Puedes detectar el número de argumentos provistos a una función (su aridad) al revisar la propiedad `.length` de la función. + +## Plantilla + +```js +function curryN(fn, n) { + // TU SOLUCIÓN +} + +module.exports = curryN; +``` diff --git a/exercises/currying/solution_fr/solution.js b/exercises/currying/solution_fr/solution.js deleted file mode 100644 index 98caefb..0000000 --- a/exercises/currying/solution_fr/solution.js +++ /dev/null @@ -1,9 +0,0 @@ -function curryN(fn, n) { - n = n || fn.length - return function curriedN(arg) { - if (n <= 1) return fn(arg) - return curryN(fn.bind(this, arg), n - 1) - } -} - -module.exports = curryN diff --git a/exercises/function_call/problem.es.md b/exercises/function_call/problem.es.md new file mode 100644 index 0000000..737429b --- /dev/null +++ b/exercises/function_call/problem.es.md @@ -0,0 +1,67 @@ +# Tarea + +Escribe una función que te permita usar `Array.prototype.slice` sin usar `slice.call` o `slice.apply` para llamarla. +Normalmente tienes que usar `slice` con `call` o `apply`: + +```js +var slice = Array.prototype.slice + +function() { + var args = slice.call(arguments) // esto funciona +} +``` + +Queremos que esto funcione: + +```js +var slice = yourFunction + +function() { + var args = slice(arguments) // esto funciona +} +``` + +## Ejemplo + +Una función `slice` que se comporta de la siguiente manera: + +```js +var nums = [1, 2, 3, 4, 5]; + +// tu función slice debería comportarse +// similar a `slice`, excepto que toma el +// array como el primer argumento. + +slice(nums, 0, 2); // [1, 2] +slice(nums, 1, 2); // [2] + +// el `slice` normal usado para comparar +nums.slice(0, 2); // [1, 2] +nums.slice(1, 2); // [2] +``` + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. +- No uses la palabra reservada `function` :D + +## Pista + +- Lo puedes resolver en una linea- +- Todas las funciones de heredan `call`, `apply` y `bind` del objeto `Function.prototype`. +- `Function#call` ejecuta el valor de this cuando es invocado, dentro de `algunaFuncion.call()`, el valor de `this` será `algunaFuncion`. +- `Function.call` por si misma es una función y hereda de `Function.prototype`. + +```js +function myFunction() { + console.log("se llamo mi función"); +} + +Function.prototype.call.call(myFunction); // => "se llamo mi función" +``` + +## Plantilla + +```js +module.exports = // tu solución aquí; +``` diff --git a/exercises/function_call/solution_es/solution.js b/exercises/function_call/solution_es/solution.js new file mode 100644 index 0000000..09c921e --- /dev/null +++ b/exercises/function_call/solution_es/solution.js @@ -0,0 +1,15 @@ +// Explicado: El valor de `this` +// en `Function.call` es la +// función que será ejecutada. + +// `Bind` se refiere a la +// nueva función que sera ejecutada +// con el valor de `this` cambiada a +// lo que sea que se paso como primer argumento` + +// Toda función "hereda" de `Function.prototype`, +// por ende toda función, incluyendo `call`, `apply` +// y `bind` tienen los métodos `call`, `apply` y `bind. + +// Function.prototype.call === Function.call +module.exports = Function.call.bind(Array.prototype.slice); diff --git a/exercises/function_spies/problem.es.md b/exercises/function_spies/problem.es.md new file mode 100644 index 0000000..491716f --- /dev/null +++ b/exercises/function_spies/problem.es.md @@ -0,0 +1,40 @@ +# Tarea + +Sobrescribe la función especifica de un objeto con una nueva funcionalidad mientras mantienes el comportamiento anterior. +Crea un espía que mantiene cuenta de cuantas veces una función fue llamada; + +## Ejemplo + +```js +var spy = Spy(console, "error"); + +console.error("llamando `console.error`"); +console.error("llamando `console.error`"); +console.error("llamando `console.error`"); + +console.log(spy.count); // 3 +``` + +## Argumentos + +- `target`: Un objeto conteniendo el método `method`. +- `method`: Un `string` con el nombre del método en `target` que debería espiar. + +## Condiciones + +- No uses ciclos `for/while` o `Array#forEach`. +- No crees ninguna función innecesaria, e.j. funciones auxiliares. + +## Pista + +- Las funciones tienen contexto, entrada y retorno. Asegurate de considerar el contexto, entrada y _el retorno de_ la función que estás espiando. + +## Plantilla + +```js +function Spy(target, method) { + // TU SOLUCIÓN +} + +module.exports = Spy; +``` diff --git a/exercises/function_spies/solution_es/solution.js b/exercises/function_spies/solution_es/solution.js new file mode 100644 index 0000000..3954a36 --- /dev/null +++ b/exercises/function_spies/solution_es/solution.js @@ -0,0 +1,19 @@ +function Spy(target, method) { + var originalFunction = target[method]; + // usa un objeto de manera que podamos pasar + //por referencia, no por valor, e.j: podemos + // devolver el resultado, pero actualizar el contador de este `scope + var result = { + count: 0 + }; + + // reemplaza el método con la función espiá. + target[method] = function() { + result.count++; // revisa que la función fue llamada + return originalFunction.apply(this, arguments); // invoca la función original + }; + + return result; +} + +module.exports = Spy; diff --git a/exercises/hello_world/problem.es.md b/exercises/hello_world/problem.es.md new file mode 100644 index 0000000..d483ebd --- /dev/null +++ b/exercises/hello_world/problem.es.md @@ -0,0 +1,17 @@ +# Tarea + +Escribe una función que tome como argumento un `string` y lo retorne en mayúsculas. + +## Argumentos + +- `input`: Un `string` de palabras aleatorias (lorem ipsum). + +## Plantilla + +```js +function upperCaser(input) { + // TU SOLUCIÓN +} + +module.exports = upperCaser; +``` diff --git a/exercises/higher_order_functions/problem.es.md b/exercises/higher_order_functions/problem.es.md new file mode 100644 index 0000000..e9ad1a5 --- /dev/null +++ b/exercises/higher_order_functions/problem.es.md @@ -0,0 +1,46 @@ +Una función `higher-order` (de alto orden) es una función que hace al menos una de las siguientes cosas: + +- Toma una o más funciones como argumentos. +- Retorna una función. + +Todas las demás funciones son de primer orden. [1] + +A diferencia de muchos otros lenguajes con capacidades imperativas, JavaScript te permite utilizar funciones `higher-order` porque tiene funciones de "primera clase". Esto significa que las funciones pueden ser tratadas como cualquier otro valor en JavaScript: justo como con números o `strings`, el valor de una función puede ser almacenada como variable, propiedades en objetos o pasadas a otras funciones como argumentos. El valor de una función es de hecho un objeto (que hereda de `Function.prototype`) así que inclusive puedes agregar propiedades y guardar valores en ellas, como con cualquier objeto. + +La diferencia clave entre las funciones y cualquier otro tipo de valor en JavaScript es la sintaxis de llamado: Si una referencia a una función le sigue unos paréntesis y valores opciones separado por coma: `algunaFuncion(a, b, ...etc)`, entonces el cuerpo de la función sera ejecutado con los argumentos provistos (si hay). + +En este ejercicio vamos a demostrar que las funciones pueden ser pasadas como valores al pasar una función como argumento. + +# Tarea + +Implementa una función que toma una función como su primer argumento, un número `num` como su segundo argumento, entonces ejecuta la función pasada `num` veces. + +Usa la plantilla de código dada a ti en la parte inferior, La mayoría o todos los ejercicios proveerán una plantilla. + +## Argumentos + +- `operation`: Una function, no toma argumentos y no retorna ningún valor de uso. +- `num`: El número de veces que se debe llamar la función `operation`. + +## Recursos + +- https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Functions_and_function_scope +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype + +## Pista + +- No lo pienses de mas, el código debería ser simple. +- Está bien usar un ciclo en tu implementación, puntos extra si usas recursion en vez. +- Quizás notes algún valor de retorno. Viene de la función que te otorgamos. +- No necesitas usar `console.log` para nada. + +## Plantilla + +```js +function repeat(operation, num) { + // TU SOLUCIÓN +} + +// No remuevas o edites esta linea +module.exports = repeat; +``` diff --git a/exercises/implement_map_with_reduce/problem.es.md b/exercises/implement_map_with_reduce/problem.es.md new file mode 100644 index 0000000..8cf7554 --- /dev/null +++ b/exercises/implement_map_with_reduce/problem.es.md @@ -0,0 +1,42 @@ +# Tarea + +Usa `Array#reduce` para implementar una versión simple de `Array#map`. + +## Resultado esperado + +Una función `map` que aplique una función a cada elemento en el `array` y recolecte el resultado en un nuevo `Array`. + +```js +var nums = [1, 2, 3, 4, 5]; + +// `map` es la función que exportas +var output = map(nums, function double(item) { + return item * 2; +}); + +console.log(output); // => [2,4,6,8,10] +``` + +## Argumentos + +- `input`: Un `Array` aleatorio de cualquier tipo. +- `operation`: Una función arbitraria la cual puede ser aplicada a los elementos en `input`. + +## Pista + +- No hay necesidad de implementar el argumento opcional `thisArg` de `Array.prototype.map`, ¡puntos adicionales si lo haces! + +## Recursos + +- https://en.wikipedia.org/wiki/Reduce_(higher-order_function) +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce + +## Plantilla + +```js +function arrayMap(arr, fn) { + // TU SOLUCIÓN +} + +module.exports = arrayMap; +``` diff --git a/exercises/partial_application_with_bind/problem.es.md b/exercises/partial_application_with_bind/problem.es.md new file mode 100644 index 0000000..f1d2f22 --- /dev/null +++ b/exercises/partial_application_with_bind/problem.es.md @@ -0,0 +1,41 @@ +# Tarea + +**Usa `Function#bind`** para implementar una función que imprima y te permita crear un `namespace` para los mensajes. + +Tu implementación debería tomar un `string` como `namespace`, y devolver una función que imprima el mensaje a la consola con el `namespace` como prefijo. + +Asegurate que _todos_ los argumentos pasados a la función que retornes sean impresos. + +**Imprime los argumentos directamente en la consola** + +## Argumentos + +- `namespace`: Un `string` que será el sufijo a cada mensaje enviado a la función retornada. + +## Ejemplo + +```js +var info = logger("INFO:"); +info("este es un mensaje informativo"); +// INFO: este es un mensaje informativo + +var warn = logger("AVISO:"); +warn("este es un aviso", "con mas información"); +// AVISO: este es un aviso con mas información +``` + +## Condiciones + +- Usa `Function#bind`. + +## Plantilla + +```js +module.exports = function(namespace) { + // TU SOLUCIÓN +}; +``` + +## Recursos + +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind diff --git a/exercises/partial_application_without_bind/problem.es.md b/exercises/partial_application_without_bind/problem.es.md new file mode 100644 index 0000000..44325da --- /dev/null +++ b/exercises/partial_application_without_bind/problem.es.md @@ -0,0 +1,108 @@ +La aplicación parcial te permite crear nuevas funciónes de funciónes existentes, mientras permite ajustar algún número de argumentos. Luego de ajustar algunos argumentos para ser aplicados parcialmente, obtienes una nueva función para obtener el resto de los argumentos y quizás ejecutar la función original. + +Formalmente: La aplicación parcial se refiere al proceso de ajustar un número de argumentos para una función, produciendo una función con una aridad (cantidad de argumentos) menor. + +Como ejemplo, digamos que tenemos una función `add`, que toma dos argumentos y los suma: + +```js +function add(x, y) { + return x + y; +} + +add(10, 20); // => 30 +``` + +Ahora, pretendamos que tenemos una función `partiallyApply` que toma una función y algunos argumentos para "aplicar parcialmente". + +Aquí "aplicamos parcialmente" el primer parámetro, `x`, a nuestra función `add`: + +```js +var addTen = partiallyApply(add, 10); // ajusta `x` a ser 10 +``` + +`addTen` es una función nueva que toma como parámetro `y` de `add`. ¡`add` no ha sido llamada aún! + +Una vez pasamos el argumento para `y`, podemos ejecutar la función original de`add`: + +```js +addTen(20); // => 30 +addTen(100); // => 110 +addTen(0); // => 10 + +// etc +``` + +Todos los ejemplos de arriba son el mismo llamando `add(10, y)`, donde `y` fue provisto en la llamada a la función `addTen`. + +# Tarea + +Usa aplicación parcial para crear una función que ajuste el primer argumento de `console.log`. e.j. Implementa una función de impresión que agregue un `namespace` como sufijo al `string` que vaya a imprimir. + +Tu implementación debería tomar un `string` como `namespace` y retornar una función que imprima el mensaje en consola con el `namespace` com prefijo. + +Deberías utilizar `Function#apply` para implementar la aplicación parcial. + +Asegurate que _todos_ los argumentos pasados a la función que retornes sean impresos. + +**Imprime los argumentos directamente en la consola** + +## Argumentos + +- `namespace`: Un `string` que será el sufijo a cada mensaje enviado a la función retornada. + +## Ejemplo + +```js +var info = logger("INFO:"); +info("este es un mensaje informativo"); +// INFO: este es un mensaje informativo + +var warn = logger("AVISO:"); +warn("este es un aviso", "con mas información"); +// AVISO: este es un aviso con mas información +``` + +## Condiciones + +- No uses `Function#bind`. +- Usa `Function#apply`. + +## Plantilla + +```js +function logger(namespace) { + // TU SOLUCIÓN +} + +module.exports = logger; +``` + +## Recursos + +- https://en.wikipedia.org/wiki/Partial_application +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply + +## Pista + +Recuerda que `console.log` toma cualquier número de argumentos y los imprime, separados por espacios: + +```js +console.log("hello", "world"); // => 'hello world' +console.log(1, 2, 3); // => 1 2 3 +``` + +Queremos simplemente "aplicar parcialmente" el primer argumento de `console.log`. + +`Function.prototype.apply` nos permite ejecutar una función, proveer un nuevo valor para `this` (que podemos ignorar en este caso), y entonces proveer _un array de argumentos para pasar a la función_: + +```js +add(10, 20); // => 30 +add.apply(null, [10, 20]); // => 30 +``` + +También ten en cuenta `apply` con `Function.prototype.call`: + +```js +add.apply(null, [10, 20]); // => 30 +add.call(null, 10, 20); // => 30 +``` diff --git a/exercises/prototypical_inheritance_by_hand/problem.es.md b/exercises/prototypical_inheritance_by_hand/problem.es.md new file mode 100644 index 0000000..82e151c --- /dev/null +++ b/exercises/prototypical_inheritance_by_hand/problem.es.md @@ -0,0 +1,205 @@ +Vamos a crear una vaga implementación de la herencia de prototipos en JavaScript a mano, para asegurar que entendemos completamente como la herencia de prototipos funcióna. + +# Tarea + +Implementa las funciónes `New`, `Create` y `Lookup` para simular `new`, `Object.create` y búsqueda de propiedades en JavaScript respectivamente. + +Por medio de esta experiencia, evitaras usar cualquier capacidad de herencia de JavaScript. En vez, necesitaras usar tus propias funciónes de `New`, `Create` y `Lookup` al igual que `__PROTO__` para representar la instancia de un prototipo, y `PROTOTYPE` para representar el constructor de un prototipo. + +## Ejemplo + +```js +New(Apple, 1, 2, 3) == new Apple(1, 2, 3); // true + +obj.__PROTO__ == obj.__proto__ || Object.getPrototypeOf(obj); // true + +Constructor.PROTOTYPE == Constructor.prototype; // true +``` + +## Parte 1: Lookup + +`Lookup` simulará el mecanismo de la búsqueda de propiedades en JavaScript, o `Getters`. Cuando tu referencias cualquier propiedad de un objeto en JavaScript, esta "recorrerá la cadena de prototipos" para encontrar una propiedad, si lo encuentra devolverá su valor, de otro modo retornara `undefined`. + +Tu función de `Lookup` le sera provista un objeto de contexto, y la propiedad en `string` que estamos buscando. Si la propiedad se encuentra en el contexto actual, retorna esa propiedad, de otro modo busca en el contexto del prototipo, `__PROTO__`. + +Si una propiedad no puede ser encontrada en la cadena de prototipos del objeto, simplemente retorna `undefined`. + +```js +var cat = { + color: "black" +}; + +var kitten = { + size: "small" +}; + +var otherKitten = { + size: "small", + color: "grey" +}; + +kitten.__PROTO__ = cat; +otherKitten.__PROTO__ = cat; + +Lookup(kitten, "color"); // => 'black' +Lookup(otherKitten, "color"); // => 'grey' + +Lookup(kitten, "wings"); // => undefined + +// cambiar propiedades en el prototipo debería +// afectar todas las instancias que hereden de el. +cat.color = "blue"; + +Lookup(kitten, "color"); // => 'blue' + +// propiedades sobrescritas deberían funciónar +Lookup(otherKitten, "color"); // => 'grey' +``` + +### Notas + +En JavaScript, cuando tu "obtienes" (`get`) un propiedad (e.j. `lookup`), el motor recorre la cadena de prototipos para encontrar el valor, pero si tu "das" (`set`) un valor, ignora la cadena de prototipo y simplemente da el valor al objeto actual, Nosotros pudimos haber implementado un `Setter` como ejercicio, pero no hay magia detrás de esto, es bastante trivial: + +```js +function Setter(context, property, value) { + return (context[property] = value); +} +``` + +## Parte 2: Create + +`Create` simulara el comportamiento de `Object.create`. + +`Create` se le pasara un objeto, y debes retornar un nuevo objeto con su prototipo(`__PROTO__`) que sea el valor del objeto provisto. + +```js +function Cat() {} + +Cat.PROTOTYPE.speak = function() { + return "Meow!"; +}; + +function Kitten() { + Cat.apply(this, arguments); +} + +Kitten.PROTOTYPE = Create(Cat.PROTOTYPE); + +var kitten = New(Kitten); +Lookup(kitten, "speak")(); // => 'Meow!' +``` + +## Parte 3: New + +`New` simulara el comportamiento de la palabra clave de JavaScript `new`. + +El primer argumento pasado a `New` sera una función constructora (e.j. un tipo). Los parámetros subsecuentes deberán ser pasados al constructor de la función cuando se cree un nuevo objeto. + +`New` retornara nuevos objetos usando la función constructora provista. + +```js +function Cat(color) { + this.color = color; +} + +var blackCat = New(Cat, "black"); +blackCat.color; // => black + +var brownCat = New(Cat, "brown"); +brownCat.color; // => brown +``` + +La función constructora pasada a `New` puede que tenga una propiedad `.PROTOTYPE` . Todos los objetos con este constructor tendrán su `__PROTO__` a que sea el valor de la propiedad `.PROTOTYPE` del constructor. + +```js +function Cat(color) { + this.color = color; +} + +Cat.PROTOTYPE.speak = function() { + return "Meow!"; +}; + +function Kitten() { + Cat.apply(this, arguments); +} + +Kitten.PROTOTYPE = Create(Cat); + +Kitten.PROTOTYPE.speak = function() { + return "Mew."; +}; + +var blackCat = New(Cat, "black"); +blackCat.color; // => black + +var brownCat = New(Cat, "brown"); +brownCat.color; // => brown +``` + +Las propiedades del prototipo deberían estar disponibles en el constructor: + +```js +function Cow() { + // busca this.moo: + console.log("moo", Lookup(this, "moo")); +} + +// este paso es hecho automaticamente en JavaScript "normal". +Cow.PROTOTYPE = {}; + +Cow.PROTOTYPE.moo = true; +var cow = New(Cow); // 'moo' true +``` + +También necesitamos simular uno comportamiento adicional de la palabra clave `new`: Si el constructor de por si retorna un valor, `New` retornara ese valor. + +```js +function Cat() { + return 3; +} +var cat = new Cat(); // 3 +var cat = New(Cat); // 3 +``` + +## Condiciones + +- No uses ninguna capacidad de Javascript de herencia de prototipos. +- No uses `new`. +- No uses `__proto__`, `Object.getPrototypeOf` ni `Object.setPrototypeOf`. + +## Pista + +- Usa `hasOwnProperty` para descubrir si un objeto tiene una propiedad. + +## Plantilla + +```js +/** + * @param context {Object} Objeto inicial para comenzar la búsqueda por "propiedad". + * @param property {String} nombre de la propiedad que estamos tratando de encontrar. + * @return {Mixed} El valor de `property` en `context` o en algún lugar de su cadena de prototipos. + */ + +function Lookup(context, property) {} + +/** + * @param proto {Object} El prototipo a usar para el objeto creado. + * @return Un nuevo objeto cuyo prototipo es puesto en`proto`. + */ + +function Create(proto) {} + +/** + * @param Constructor {Function} Constructor para un nuevo tipo. + * @return nueva instancia de un tipo definida por `Constructor`. + */ + +function New(Constructor) {} + +module.exports = { + Lookup: Lookup, + Create: Create, + New: New +}; +``` diff --git a/exercises/recursion/problem.es.md b/exercises/recursion/problem.es.md new file mode 100644 index 0000000..8f2af97 --- /dev/null +++ b/exercises/recursion/problem.es.md @@ -0,0 +1,55 @@ +# Tarea + +Implementa una función recursiva que retorne todas las dependencias únicas, y sub-dependencias de un modulo, organizado alfabéticamente. Las dependencias deberían ser impresas en forma `dependencia@version`, por ejemplo `inflection@1.3.6`. + +Se permite tener múltiples versiones de un mismo modulo, pero los módulos duplicados de la misma version deben ser removidos. + +## Argumentos: + +- `tree`: Una árbol de dependencias. Abajo encontrarás un ejemplo de la estructura. + +## Ejemplo + +```js +var loremIpsum = { + name: "lorem-ipsum", + version: "0.1.1", + dependencies: { + optimist: { + version: "0.3.7", + dependencies: { + wordwrap: { + version: "0.0.2" + } + } + }, + inflection: { + version: "1.2.6" + } + } +}; + +getDependencies(loremIpsum); // => [ 'inflection@1.2.6', 'optimist@0.3.7', 'wordwrap@0.0.2' ] +``` + +## Condiciones: + +- No uses ciclos `for/while` o `Array#forEach`. + +## Plantilla + +```js +function getDependencies(tree) { + // TU SOLUCIÓN + // Nota: Sientete libre de agregar + // argumentos adicionales a esta funcion + // para usar con un llamado recursivo, + // ¡o no lo hagas!, hay varias maneras de hacer recursión. +} + +module.exports = getDependencies; +``` + +## Recursos + +- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys diff --git a/exercises/trampoline/problem.es.md b/exercises/trampoline/problem.es.md new file mode 100644 index 0000000..427abb3 --- /dev/null +++ b/exercises/trampoline/problem.es.md @@ -0,0 +1,59 @@ +La plantilla incluye una definición de `repeat`. `repeat` tomará como argumentos una función y un número `num`, e invocará la función `num` veces: + +```js +var count = 0; +repeat(function() { + count++; +}, 100); + +console.log("se ejecutó %d veces.", count); +// => se ejecuto 100 veces. +``` + +PERO nota que ejecutar `repeat` con un número grande en `num` genera un `stack overflow`: + +``` +var count = 0 +repeat(function() { + count++ +}, 100000) + +console.log('se ejecutó %d veces', count) +// => RangeError: Maximum call stack size exceeded +``` + +# Tarea + +Modifica la plantilla abajo de forma que use un trampolín para llamarse continuamente a si mismo de forma sincronizada. + +Puedes asumir que la operación pasada a `repeat` no recibe argumentos (o que han sido conectados previamente a la función) y el valor de retorno de la función no es importante. + +## Condiciones + +- No cambies la implementación de `repeat` para que incluya ciclos (aunque la puedes cambiar de otras maneras). + +## Pista + +- Modifica `repeat` para que retorne el "siguiente paso, si lo hay. +- Un trampolín continua ejecutando los pasos de forma sincronizada, obteniendo nuevos pasos hasta que no haya mas pasos. !Puedes usar un ciclo aquí!. +- Si tu programa toma mucho tiempo en correr, probablemente algo se encuentre mal. Usa `Ctrl+C` para matar el proceso de `Node`. + +## Plantilla + +```js +function repeat(operation, num) { + // Modifica esto de manera que no cause un `stack overflow`! + if (num <= 0) return; + operation(); + return repeat(operation, --num); +} + +function trampolíne(fn) { + // ¡Probablemente quieras implementar un trampolín! +} + +module.exports = function(operation, num) { + // ¡Probablemente quieras llamar tu trampolín aquí! + return repeat(operation, num); +}; +``` diff --git a/functional-javascript.js b/functional-javascript.js index 2c6edfa..56e4a7c 100755 --- a/functional-javascript.js +++ b/functional-javascript.js @@ -8,5 +8,5 @@ var path = require('path') Workshopper({ name : 'functional-javascript' , appDir : __dirname - , languages : ['en', 'fr', 'ko'] + , languages : ['en', 'fr', 'ko', 'es'] }) diff --git a/i18n/es.json b/i18n/es.json new file mode 100644 index 0000000..7892e05 --- /dev/null +++ b/i18n/es.json @@ -0,0 +1,70 @@ +{ + "title": "JAVASCRIPT FUNCIONAL ES GENIAL", + "subtitle": "\u001b[23mElige un ejercicio y oprime \u001b[3mEnter\u001b[23m para comenzar", + "common": { + "exercise": { + "fail": { + "missing_deps": "Necesitas instalar todas las dependencias que estés usando en tu solución (e.j. lodash)", + "module_not_found": "No se pudo encontrar tu archivo. Asegúrate de que la ruta es correcta.", + "must_export_function": "Siempre deberías retornar una función usando el objeto module.exports." + }, + "input": "input: %s", + "submission": "sumisión: %s", + "solution": "solución: %s" + } + }, + "exercise": { + "Hello World": "Hola Mundo", + "Higher Order Functions": "Funciones `higher-order` (de alto orden)", + "Basic: Map": "Fundamentos: `Map`", + "Basic: Filter": "Fundamentos: `Filter`", + "Basic: Every Some": "Fundamentos: `Every`, `Some`", + "Basic: Reduce": "Fundamentos: `Reduce`", + "Basic: Recursion": "Fundamentos: Recursividad", + "Basic: Call": "Fundamentos: `Call`", + "Partial Application without Bind": "Aplicación parcial sin Bind", + "Partial Application with Bind": "Aplicación parcial con Bind", + "Implement Map with Reduce": "Implementar `Map` con la ayuda de `Reduce`", + "Function Spies": "Espiás en las funciones", + "Blocking Event Loop": "Bloqueando ciclos de eventos", + "Trampoline": "Trampolines", + "Async Loops": "Ciclos asíncronos", + "Recursion": "Recursividad", + "Currying": "`Currying`", + "Function Call": "Llamada de funciones" + }, + "exercises": { + "Basic: Map": { + "didnt_use_map": "No usaste Array#map", + "used_map": "¡Sí! Usaste Array#map" + }, + "Basic: Every Some": { + "found_good_lists": "!Encontré %d buenas listas!" + }, + "Basic: Call": { + "matched_objects": "Encontrados %d de %d objetos validos de %d." + }, + "Higher Order Functions": { + "call_log": "La función se llamo %d veces." + }, + "Function Spies": { + "call_times": "El método fue llamado %d veces.", + "incorrect_return": "¡Verifica el valor de retorno de tu función!", + "incorrect_this": "¡Verifica el valor de `this` en tu función! Pista: Function#apply", + "not_all_args": "¡Verifica que estés pasando TODOS los argumentos! Pista: Function#apply", + "incorrect_count": "!Verifica que tu `Spy` esté contando las invocaciones del método correctamente!" + }, + "Trampoline": { + "intro": "Se repitió %d veces", + "result": "Se ejecutó exitosamente %d veces." + }, + "Async Loops": { + "all_loaded": "¡Todos los %d usuarios fueron cargados!", + "bad_result": "esperado: \n%s\n pero se recibió:\n%s", + "took_too_long": "¡Tomó demasiado tiempo!" + }, + "Currying": { + "five_words": "Este,problema,ha,sido,resuelto" + } + } +}