diff --git a/Readme.md b/Readme.md index 6499415..24471e5 100644 --- a/Readme.md +++ b/Readme.md @@ -48,23 +48,23 @@ will build on skills developed by solving previous problems. #### 2. Writing your solution -Once you have selected a problem, the workshop will remember which problem you are working on. +Once you have selected a problem, the workshop will remember which problem you are working on. Using your preferred editor, simply create a file to write your solution in. Most problems will supply some boilerplate with which to get started. Copy this from the problem description to your solution file. #### 3. Testing your solution -Use the workshop's `run` command to point the workshop at your solution file. Your solution will loaded +Use the workshop's `run` command to point the workshop at your solution file. Your solution will be loaded and passed the problem input. This usually won't perform any validation, it will only show the program output. ``` $ functional-javascript-workshop run mysolution.js ``` - + #### 4. Verifying your solution -Your solution will be verified against the output of the 'official' solution. +Your solution will be verified against the output of the 'official' solution. If all of the output matches, then you have successfully solved the problem! ``` @@ -73,7 +73,7 @@ $ functional-javascript-workshop verify mysolution.js ## Stuck? -Feedback and criticism is welcome, please log your troubles in [issues](https://github.com/timoxley/functional-javascript-workshop/issues). +Feedback and criticism is welcome, please log your troubles in [issues](https://github.com/timoxley/functional-javascript-workshop/issues). Full curriculum reviews [like this one](https://github.com/timoxley/functional-javascript-workshop/issues/7) are incredibly helpful. More feedback like this please! @@ -87,7 +87,7 @@ We're looking for more practical problems, so if you come across a problem in yo ## Resources -[A growing collection of quality functional javascript resources can be found in the wiki](https://github.com/timoxley/functional-javascript-workshop/wiki). +[A growing collection of quality functional javascript resources can be found in the wiki](https://github.com/timoxley/functional-javascript-workshop/wiki). ## Thanks rvagg @@ -120,6 +120,6 @@ This tutorial was built using rvagg's [workshopper](https://github.com/rvagg/wor 1 Matthew Hokanson 0.7% ``` -## Licence +## License MIT diff --git a/exercises/async_loops/problem.ko.md b/exercises/async_loops/problem.ko.md new file mode 100644 index 0000000..c4eac36 --- /dev/null +++ b/exercises/async_loops/problem.ko.md @@ -0,0 +1,52 @@ +이 코드는 동작하지 않습니다! + +어떤 자바 개발자가 이 끔찍한 코드를 우리에 코드베이스에 테스트도 안 하고 커밋했습니다! + +```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 +``` + +# 해야할 일 + +이 코드를 고치세요! 콜백은 모든 사용자가 로드된 후에 호출되어야만 합니다. +사용자의 순서는 공급된 사용자의 아이디의 순서와 일치해야 합니다. 이 함수는 비동기적이기 때문에, 반환 값을 걱정하지 않아도 됩니다. + +## 인자 + +* userIds: 사용자 아이디(숫자)의 배열 +* load: 사용자 객체를 불러올 때 사용하는 함수. 숫자 아이디와 콜백을 받음. 콜백은 특정 아이디로 로딩된 사용자(사용자거나 널)와 함께 호출됩니다. +* done: (`load`로 검색된) 사용자 객체의 배열을 받는 함수. + +## 조건 + +* for/while 반복문을 사용하지 마세요. (Array#forEach는 괜찮습니다.) +* `done`의 결과의 순서는 `userIds`의 순서와 같아야 합니다. +* 사용자들은 병렬로 로드되어야 합니다. 전체 작업은 1초 이상 걸리면 안 됩니다. +* 헬퍼 같은 불필요한 함수를 만들지 마세요. + +## 힌트 + +* 순서를 관리하기 위해 정렬할 필요는 없습니다. +* `console.log`를 사용하면 확인에 영향을 줍니다. `console.log`는 `functional-javascript run` 할 때만 사용하세요. + +## 템플릿 + +```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/async_loops/problem.md b/exercises/async_loops/problem.md index 6f12f5b..539a8a9 100644 --- a/exercises/async_loops/problem.md +++ b/exercises/async_loops/problem.md @@ -30,7 +30,7 @@ The order of the users should match the order of supplied user ids. Because this * Do not use for/while loops (Array#forEach ok). * The order of the results in `done` must be the same as they were specified in `userIds`. * Users should be loaded in parallel i.e. the entire job should not take more than 1 second. -* Do not create any unecessary functions e.g. helpers. +* Do not create any unnecessary functions e.g. helpers. ## Hint diff --git a/exercises/basic_call/problem.ko.md b/exercises/basic_call/problem.ko.md new file mode 100644 index 0000000..837f6a8 --- /dev/null +++ b/exercises/basic_call/problem.ko.md @@ -0,0 +1,105 @@ +JavaScript는 'duck' 타이핑을 구현합니다. 덕 타이핑은 동적 타이핑의 형식입니다. 이는 특정 클래스에서 상속받는 것이나 특정 인터페이스의 구현 방식이 아닌, 객체의 메소드와 속성을 통해 바른 형식이 무엇인지 결정합니다. 이 개념의 이름은 James Whitcomb Riley가 만든 덕 데스트에 기인하고 다음 문장으로 요약할 수 있습니다. + + "오리처럼 걷고, 오리처럼 수영하고, 오리처럼 꽥꽥대는 새가 있다면, 나는 그 새를 오리라 부르겠다" + +JavaScript에서 탄탄한 프로그램을 작성하려다보면 종종 객체가 우리가 원하는 형인지 확인할 필요가 있습니다. + +객체가 직접 구현한 프로퍼티를 '가지고' 있는지를 확인하려면 (즉, 프로토타입에서 상속되지 않았는지 확인하려면) Object#hasOwnProperty를 사용할 수 있습니다. + +```js +var duck = { + quack: function() { + console.log('quack') + } +} + +duck.hasOwnProperty('quack') // => true +``` + +duck에 .hasOwnProperty 메소드를 구현한 적이 없는데 어디서 왔을까요? + +duck은 `{}` 구문으로 만들어졌으므로 Object.prototype을 상속받습니다. + +```js +var object = {quack: true} + +Object.getPrototypeOf(object) === Object.prototype // => true +object.hasOwnProperty('quack') // => true +``` + +Object.prototype에서 상속되지 않은 객체라면 어떨까요? + +```js +// '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' +``` + +'객체처럼 보이는 것'에 `this` 값과 같이 호출할 수 있다면 `Object.prototype`를 통해 `hasOwnProperty`를 사용할 수 있습니다. Function#call을 사용하면 변경된 `this` 값으로 어떤 함수든 호출할 수 있습니다. + +```js +// 호출할 때 넘기는 첫 번째 인자는 `this`의 값이 됩니다 +// 나머지 인자는 함수에 넘기는 인자가 됩니다 + +Object.prototype.hasOwnProperty.call(object, 'quack') // => true +``` + +# 해야할 일: + +'quack' 프로퍼티가 직접 정의된 인자의 개수를 반환하는 `duckCount` 함수를 작성하세요. 프로토타입에서 상속된 값은 제외하셔야 합니다. + +예제: + +```js +var notDuck = Object.create({quack: true}) +var duck = {quack: true} +duckCount(duck, notDuck) // 1 +``` +## 인자 + +* 0-20개의 인자를 넘깁니다. 각 인자는 어떤 프로퍼티도 가질 수 있는 모든 형이 가능합니다. 몇몇 인자는 'quack' 프로퍼티를 가집니다. + +## 조건 + +* for/while 반복문이나 Array#forEach를 사용하지 마세요. +* counter/accumulator 변수를 만들지 마세요. +* 헬퍼 같은 불필요한 함수를 만들지 마세요. + +## 힌트 + +* 모든 함수에서 사용할 수 있는 `arguments` 변수는 *배열*처럼 'quack'하는 *객체*입니다. + +```js +{ + 0: 'argument0', + 1: 'argument1', // etc + length: 2 +} +``` + +## 참고 + +* 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 + + +## 템플릿 + +```js +function duckCount() { + // 여기에 해답을 적으세요 +} + +module.exports = duckCount +``` diff --git a/exercises/basic_call/problem.md b/exercises/basic_call/problem.md index 6376f22..1b81015 100644 --- a/exercises/basic_call/problem.md +++ b/exercises/basic_call/problem.md @@ -71,7 +71,7 @@ duckCount(duck, notDuck) // 1 * Do not use any for/while loops or Array#forEach. * Do not create any counter/accumulator variables. -* Do not create any unecessary functions e.g. helpers. +* Do not create any unnecessary functions e.g. helpers. ## Hint diff --git a/exercises/basic_every_some/problem.ko.md b/exercises/basic_every_some/problem.ko.md new file mode 100644 index 0000000..08458bb --- /dev/null +++ b/exercises/basic_every_some/problem.ko.md @@ -0,0 +1,59 @@ +# 해야할 일 + +유효한 사용자의 목록을 받아, 주어진 사용자가 원래 사용자의 목록에 있으면 true를 반환하는 함수를 반환하세요. + +아이디가 같은지만 확인하면 됩니다. + +## 예제 + +```js +var goodUsers = [ + { id: 1 }, + { id: 2 }, + { id: 3 } +] + +// 선언할 함수 이름은 `checkUsersValid`입니다 +var testAllValid = checkUsersValid(goodUsers) + +testAllValid([ + { id: 2 }, + { id: 1 } +]) +// => true + +testAllValid([ + { id: 2 }, + { id: 4 }, + { id: 1 } +]) +// => false +``` + +## 인자 + +* goodUsers: 유효한 사용자의 목록 + +Array#some과 Array#every를 사용해 export된 함수에 넘겨진 배열에 반환된 함수에 넘겨진 모든 사용자가 있는지 확인하세요. + +## 조건 + +* for/while 반복문이나 Array#forEach를 사용하지 마세요. +* 헬퍼 같은 불필요한 함수를 만들지 마세요. + +## 참고 + +* 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 + +## 템플릿 + +```js +function checkUsersValid(goodUsers) { + return function allUsersValid(submittedUsers) { + // 여기에 해답을 적으세요 + }; +} + +module.exports = checkUsersValid +``` diff --git a/exercises/basic_every_some/problem.md b/exercises/basic_every_some/problem.md index 101193c..8c1b837 100644 --- a/exercises/basic_every_some/problem.md +++ b/exercises/basic_every_some/problem.md @@ -39,7 +39,7 @@ Use array#some and Array#every to check every user passed to your returned funct ## Conditions * Do not use any for/while loops or Array#forEach. -* Do not create any unecessary functions e.g. helpers. +* Do not create any unnecessary functions e.g. helpers. ## Resources diff --git a/exercises/basic_filter/problem.ko.md b/exercises/basic_filter/problem.ko.md new file mode 100644 index 0000000..e254b4e --- /dev/null +++ b/exercises/basic_filter/problem.ko.md @@ -0,0 +1,49 @@ +# 해야할 일 +Array#filter를 사용해 `getShortMessages`를 호출하는 함수를 작성하세요. + +`getShortMessages`는 '.message' 프로퍼티를 가지는 배열을 받아 *50자보다 적은* 메시지의 배열을 반환합니다. + +이 함수는 *메시지를 가진 객체가 아닌,* 메시지의 배열을 반환해야 합니다. + +## 인자 + +* messages: 밑에 있는 것처럼 생긴 임의의 객체를 10~100개 가지는 배열 + +```js +{ + message: 'Esse id amet quis eu esse aute officia ipsum.' // 랜덤 +} +``` + +## 조건 + +* for/while 반복문이나 Array#forEach를 사용하지 마세요. +* 헬퍼 같은 불필요한 함수를 만들지 마세요. + +## 힌트 + +* 배열 메소드를 몇 개 연결해 보세요. + +## 예제 + +``` +[ '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.' ] +``` + +## 참고 + +* 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 + +## 템플릿 + +```js +function getShortMessages(messages) { + // 여기에 해답을 적으세요 +} + +module.exports = getShortMessages +``` diff --git a/exercises/basic_inheritance_with_objectcreate/problem.md b/exercises/basic_inheritance_with_objectcreate/problem.md index 892071e..fbed304 100644 --- a/exercises/basic_inheritance_with_objectcreate/problem.md +++ b/exercises/basic_inheritance_with_objectcreate/problem.md @@ -51,7 +51,7 @@ console.log('Hello ' + joe) // 'Hello [BetterUser: Mr. Joe Smith]' * Don't call the User constructor unnecessarily! * Don't use `__proto__` -* Do not create any unecessary functions e.g. helpers. +* Do not create any unnecessary functions e.g. helpers. ## Resources diff --git a/exercises/basic_map/exercise.js b/exercises/basic_map/exercise.js index c034253..75e4d03 100644 --- a/exercises/basic_map/exercise.js +++ b/exercises/basic_map/exercise.js @@ -3,7 +3,7 @@ var random = require('../randomizer') var runner = require('../runner') -var input = random.arrayOfInts(19, 3, 9) +var input = random.arrayOfInts(20, 0, 9) var usedMap = false var regularMap = Array.prototype.map diff --git a/exercises/basic_map/problem.ko.md b/exercises/basic_map/problem.ko.md new file mode 100644 index 0000000..5a71131 --- /dev/null +++ b/exercises/basic_map/problem.ko.md @@ -0,0 +1,39 @@ +# 해야할 일 + +다음 코드의 for 반복문을 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 +``` + +## 인자 + +* numbers: 0에서 9사이의 정수를 0~20개 가지는 배열 + +## 조건 + +* Array.prototype.map()을 사용하셔야 합니다. +* for/while 반복문이나 Array.prototype.forEach를 사용하지 마세요. +* 헬퍼 같은 불필요한 함수를 만들지 마세요. + +## 참고 + +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map + +## 템플릿 + +```js +function doubleAll(numbers) { + // 여기에 해답을 적으세요 +} + +module.exports = doubleAll +``` diff --git a/exercises/basic_map/problem.md b/exercises/basic_map/problem.md index 6882102..09d17ef 100644 --- a/exercises/basic_map/problem.md +++ b/exercises/basic_map/problem.md @@ -16,13 +16,13 @@ module.exports = doubleAll ## Arguments -* numbers: An Array of 1 to 20 Integers between 0 and 9 +* numbers: An Array of 0 to 20 Integers between 0 and 9 ## Conditions * Your solution should use Array.prototype.map() * Do not use any for/while loops or Array.prototype.forEach. -* Do not create any unecessary functions e.g. helpers. +* Do not create any unnecessary functions e.g. helpers. ## Resources diff --git a/exercises/basic_recursion/problem.fr.md b/exercises/basic_recursion/problem.fr.md index b9ae85b..ead3278 100644 --- a/exercises/basic_recursion/problem.fr.md +++ b/exercises/basic_recursion/problem.fr.md @@ -47,7 +47,6 @@ reduce([1,2,3], function(prev, curr, index, arr) { * N’utilisez pas de boucle (`for`, `while`…) * N’utilisez aucune méthode de `Array`, du genre `Array#map()` ou `Array#reduce()` -* Ne créez aucune fonction superflue ## Ressources diff --git a/exercises/basic_recursion/problem.ko.md b/exercises/basic_recursion/problem.ko.md new file mode 100644 index 0000000..024ea98 --- /dev/null +++ b/exercises/basic_recursion/problem.ko.md @@ -0,0 +1,63 @@ +재귀는 알고리즘 문제를 우아하고 효과적으로 풀 수 있는 기초 프로그래밍 개념입니다. 사실, 재귀는 모든 반복 행동을 재귀 함수만으로 정의할 수 있을 정도로 강력합니다. 중첩된 데이터 구조를 반복할 때, 재귀는 필수적이라는 걸 아실 수 있을 겁니다. + +재귀 함수는 자기 자신을 호출하는 함수입니다. 예를 들어, 이 재귀 함수는 단어의 배열을 받아, 단어를 대문자로 바꾼 배열을 반환합니다. + +```js +function toUpperArray(items) { + if (!items.length) return [] // 종료 조건 + var head = items[0] // 처리할 아이템 + head = head.toUpperCase() // 행동 수행 + var tail = items.slice(1) // 다음 + return [head].concat(toUpperArray(tail)) // 귀납적 단계 +} + +toUpperArray(['hello', 'world']) // => ['HELLO', 'WORLD'] +``` + +이 연습 문제의 핵심은 인숙한 인터페이스를 재귀 함수로 구현해 재귀에 익숙해지는 것입니다. + +# 해야할 일 + +재귀를 이용해 Array#reduce를 구현하세요. + +reduce가 올바르게 동작하는지 테스트하기 위해 구현하신 reduce를 basic_reduce 해결책의 실행에 사용합니다. 즉, reduce 함수에 단어의 배열, 함수, 배열 안의 각 단어의 횟수를 반환할 객체의 초기값이 넘겨집니다. 구현하실 reduce에 넘겨지기 때문에 이 부분을 구현하실 필요는 없습니다. + +단순하게 하기 위해, 구현하실 reduce에 **초기값이 없을 경우에 대한 대비를 하실 필요는 없습니다**. 초기값이 언제나 있으리라 가정하셔도 됩니다. + +## 인자 + +* arr: reduce 할 배열 +* fn: reduce 단계에서 사용할 함수. 일반 Array#reduce처럼, 이 함수에는 previousValue, currentValue, index, 반복할 배열을 넘겨야 합니다. +* init: reduce의 초기값. Array#reduce와는 다르게, 이 값은 필요합니다. (그리고 항상 제공된다고 가정합니다.) + +## 예제 + +```js +// reduce 함수는 첫 번째 인자로 배열을 받는 것을 +// 제외하고 일반 Array#reduce와 같은 동작을 합니다 + +reduce([1,2,3], function(prev, curr, index, arr) { + return prev + curr +}, 0) +// => 6 +``` + +## 조건 + +* for/while 반복문을 사용하지 마세요. +* Array#map이나 Array#reduce 같은 Array 메소드를 사용하지 마세요. + +## 참고 + +* https://en.wikipedia.org/wiki/Recursion +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce + +## 템플릿 + +```js +function reduce(arr, fn, initial) { + // 여기에 해답을 적으세요 +} + +module.exports = reduce +``` diff --git a/exercises/basic_recursion/problem.md b/exercises/basic_recursion/problem.md index 0ef5b2a..8b5f1f0 100644 --- a/exercises/basic_recursion/problem.md +++ b/exercises/basic_recursion/problem.md @@ -47,7 +47,6 @@ reduce([1,2,3], function(prev, curr, index, arr) { * Do not use any for/while loops. * Do not use any Array methods like Array#map or Array#reduce. -* Do not create any unecessary functions e.g. helpers. ## Resources diff --git a/exercises/basic_reduce/problem.ko.md b/exercises/basic_reduce/problem.ko.md new file mode 100644 index 0000000..85f3ca9 --- /dev/null +++ b/exercises/basic_reduce/problem.ko.md @@ -0,0 +1,42 @@ +# 해야할 일 + +주어진 문자열의 배열을 `Array#reduce`를 이용해 각 문자열이 배열에 나타난 숫자를 가지는 객체를 만드세요. 직접 객체를 반환해야 합니다.(console.log는 필요없어요.) + +## 예제 + +```js +var inputWords = ['Apple', 'Banana', 'Apple', 'Durian', 'Durian', 'Durian'] + +console.log(countWords(inputWords)) + +// => +// { +// Apple: 2, +// Banana: 1, +// Durian: 3 +// } +``` + +## 인자 + +* inputWords: 임의의 문자열 배열 + +## 조건 + +* for/while 반복문이나 Array#forEach를 사용하지 마세요. +* 헬퍼 같은 불필요한 함수를 만들지 마세요. + +## 참고 + +* https://en.wikipedia.org/wiki/Reduce_(higher-order_function) +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce + +## 템플릿 + +```js +function countWords(inputWords) { + // 여기에 해답을 적으세요 +} + +module.exports = countWords +``` diff --git a/exercises/basic_reduce/problem.md b/exercises/basic_reduce/problem.md index a683b8b..f1b7278 100644 --- a/exercises/basic_reduce/problem.md +++ b/exercises/basic_reduce/problem.md @@ -24,7 +24,7 @@ console.log(countWords(inputWords)) ## Conditions * Do not use any for/while loops or Array#forEach. -* Do not create any unecessary functions e.g. helpers. +* Do not create any unnecessary functions e.g. helpers. ## Resources diff --git a/exercises/blocking_event_loop/problem.ko.md b/exercises/blocking_event_loop/problem.ko.md new file mode 100644 index 0000000..be04d25 --- /dev/null +++ b/exercises/blocking_event_loop/problem.ko.md @@ -0,0 +1,34 @@ +# 해야할 일 + +템플릿으로 주어진 재귀 `repeat` 함수를, 이벤트 루프를 중단하지 않도록(Timer와 IO 핸들러가 발동할 수 있도록) 수정하세요. repeat를 비동기적으로 만들 필요가 있습니다. + +테스트의 결과를 출력하고 프로세스를 종료하는 타임아웃은 100 밀리 초 후에 발동하도록 예약되어 있습니다. 모든 연산이 완료되기 전에 타임아웃이 작업을 중단할 수 있도록 `repeat`는 이벤트 루프의 제어권을 해제해야 합니다. + +timeout이 발동하기 전에 가능한 많은 작업을 수행해 보십시오! + +## 조건 + +* 어떤 for/while 반복문이나 Array#forEach도 사용하지 마세요. +* 헬퍼 같은 불필요한 함수를 만들지 마세요. + +## 힌트 + +* 프로그램의 수행시간이 오래 걸린다면, 뭔가 잘못된 것입니다. + 노드 프로세스를 죽이려면 Control - C를 사용하세요. + +## 참고 + +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Timers + +## 템플릿 + +```js +function repeat(operation, num) { + // 도중에 중단될 수 있도록 이곳을 고치세요 + if (num <= 0) return + operation() + return repeat(operation, --num) +} + +module.exports = repeat +``` diff --git a/exercises/blocking_event_loop/problem.md b/exercises/blocking_event_loop/problem.md index 4011ad7..6ff2863 100644 --- a/exercises/blocking_event_loop/problem.md +++ b/exercises/blocking_event_loop/problem.md @@ -9,7 +9,7 @@ Try to perform as many operations as you can before the timeout fires! ## Conditions * Do not use any for/while loops or Array#forEach. -* Do not create any unecessary functions e.g. helpers. +* Do not create any unnecessary functions e.g. helpers. ## Hints diff --git a/exercises/currying/problem.ko.md b/exercises/currying/problem.ko.md new file mode 100644 index 0000000..1cb82f9 --- /dev/null +++ b/exercises/currying/problem.ko.md @@ -0,0 +1,74 @@ +이 프로그램은 세 인자를 curries up하는 `curry3`의 구현 예제입니다. + +```js +function curry3(fun){ + return function(one){ + return function(two){ + return function (three){ + return fun(one, two, three) + } + } + } +} +``` + +이 구현을 이런 간단한 함수에 사용하려 한다고 해봅시다. + +```js +function abc(one, two, three) { + return one/two/three +} +``` + +이는 이렇게 동작할 것입니다. + +```js +var curryC = curry3(abc) +var curryB = curryC(6) +var curryA = curryB(3) + +console.log(curryA(2)) // => 1 +``` + +# 해야할 일 + +이 도전 과제에서는, 임의의 수의 인자를 받는 'curry' 함수를 구현할 것입니다. + +`curryN`은 두 파라미터를 받습니다. + +* fn: curry할 함수. +* n: 생략할 수 있는 curry할 인자의 수. 주어지지 않으면 `curryN`은 fn의 인자를 `n`의 값으로 사용해야 함. + +## 예제 + +```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 +``` + +## 조건 + +* 어떤 for/while 반복문이나 Array#forEach도 사용하지 마세요. + +## 힌트 + +* 함수의 예상되는 인자의 수는 함수의 .length 속성을 확인해서 알 수 있습니다. + +## 템플릿 + +```js +function curryN(fn, n) { + // 여기에 해답을 적으세요 +} + +module.exports = curryN +``` diff --git a/exercises/function_call/problem.ko.md b/exercises/function_call/problem.ko.md new file mode 100644 index 0000000..6799dde --- /dev/null +++ b/exercises/function_call/problem.ko.md @@ -0,0 +1,67 @@ +# 해야할 일 + +`Array.prototype.slice`를 사용하는 함수를 `slice.call`이나 `slice.apply`를 사용하지 않고 작성하세요. + +일반적으로는 `slice`에는 `call`이나 `apply`가 필요합니다. + +```js +var slice = Array.prototype.slice + +function() { + var args = slice.call(arguments) // this works +} +``` + +이렇게 동작하게 하고 싶습니다. + +```js +var slice = yourFunction + +function() { + var args = slice(arguments) // this works +} +``` + +## 예제 + +`slice` 함수는 다음과 같이 사용할 수 있습니다. + +```js +var nums = [1,2,3,4,5] + +// slice 함수는 첫 번째 인자로 배열을 받는 것을 제외하고 +// slice의 일반적인 행동과 같아야 합니다. + +slice(nums, 0, 2) // [1, 2] +slice(nums, 1, 2) // [2] + +// 비교를 위한 일반적인 slice의 사용법 +nums.slice(0, 2) // [1, 2] +nums.slice(1, 2) // [2] +``` + +## 조건 + +* 어떤 for/while 반복문이나 Array#forEach도 사용하지 마세요. +* `function` 키워드를 사용하지 마세요. :D + +## 힌트 + +* 한 줄이면 충분합니다. +* 모든 JavaScript 함수는 call, apply, bind 같은 메소드를 `Function.prototype` 객체로부터 상속합니다. +* Function#call은 호출될 때 `this`의 값을 실행합니다. `someFunction.call()` 안에서 `this`의 값은 `someFunction`이 됩니다. +* Function.call 자체는 `Function.prototype`에서 상속되는 함수입니다. + +```js +function myFunction() { + console.log('called my function') +} + +Function.prototype.call.call(myFunction) // => "called my function" +``` + +## 템플릿 + +```js +module.exports = // 여기에 해답을 적으세요 +``` diff --git a/exercises/function_spies/exercise.js b/exercises/function_spies/exercise.js index 4d8bda2..fcbda7a 100644 --- a/exercises/function_spies/exercise.js +++ b/exercises/function_spies/exercise.js @@ -32,7 +32,13 @@ var exercise = module.exports = runner.custom(function(Spy, input) { exercise.emit('fail', exercise.__('incorrect_return')) } }) + count++ + + if (!spy.count || spy.count !== count) { + exercise.emit('fail', exercise.__('incorrect_count')) + } else { + result.push(exercise.__('call_times', spy.count)) + } - result.push(exercise.__('call_times', spy.count)) return result }).quiet(input) diff --git a/exercises/function_spies/problem.ko.md b/exercises/function_spies/problem.ko.md new file mode 100644 index 0000000..ebbff34 --- /dev/null +++ b/exercises/function_spies/problem.ko.md @@ -0,0 +1,41 @@ +# 해야할 일 + +객체의 특정 메소드를 오버라이드해 이전 기능을 유지하면서 새로운 기능을 추가하세요. + +얼마나 함수가 호출되었는지 감시하는 스파이를 만드세요. + +## 예제 + +```js +var spy = Spy(console, 'error') + +console.error('calling console.error') +console.error('calling console.error') +console.error('calling console.error') + +console.log(spy.count) // 3 +``` + +## 인자 + +* target: `method` 메소드를 가지는 객체 +* method: `target`에 있는 감시할 메소드 이름(문자열) + +## 조건 + +* 어떤 for/while 반복문이나 Array#forEach도 사용하지 마세요. +* 헬퍼 같은 불필요한 함수를 만들지 마세요. + +## 힌트 + +* 함수는 문맥, 입력, 출력을 가집니다. 감시하는 함수의 문맥, 입력, *출력*을 확실히 고려하세요. + +## 템플릿 + +```js +function Spy(target, method) { + // 여기에 해답을 적으세요 +} + +module.exports = Spy +``` diff --git a/exercises/function_spies/problem.md b/exercises/function_spies/problem.md index c6b9387..fdaf307 100644 --- a/exercises/function_spies/problem.md +++ b/exercises/function_spies/problem.md @@ -24,7 +24,7 @@ console.log(spy.count) // 3 ## Conditions * Do not use any for/while loops or Array#forEach. -* Do not create any unecessary functions e.g. helpers. +* Do not create any unnecessary functions e.g. helpers. ## Hint diff --git a/exercises/hello_world/problem.ko.md b/exercises/hello_world/problem.ko.md new file mode 100644 index 0000000..2ae6bc3 --- /dev/null +++ b/exercises/hello_world/problem.ko.md @@ -0,0 +1,17 @@ +# 해야할 일 + +입력을 받아 대문자로 바꿔 출력하는 함수를 작성하세요. + +## 인자 + +* input: 임의의 영단어 문자열(lorem ipsum). + +## 템플릿 + +```js +function upperCaser(input) { + // 여기에 해답을 적으세요 +} + +module.exports = upperCaser +``` diff --git a/exercises/higher_order_functions/exercise.js b/exercises/higher_order_functions/exercise.js index 02924bb..68688b4 100644 --- a/exercises/higher_order_functions/exercise.js +++ b/exercises/higher_order_functions/exercise.js @@ -11,3 +11,5 @@ var exercise = module.exports = runner.init(function() { }).quiet(function count() { console.log(exercise.__('call_log', ++counter)) }, random.int(3, 10)) + +exercise.ignoreReturnValue = true diff --git a/exercises/higher_order_functions/problem.ko.md b/exercises/higher_order_functions/problem.ko.md new file mode 100644 index 0000000..74c22d0 --- /dev/null +++ b/exercises/higher_order_functions/problem.ko.md @@ -0,0 +1,46 @@ +고차(higher-order) 함수는 다음 중 하나에 해당하는 함수입니다. + +* 입력으로 하나 이상의 함수를 받음 +* 함수를 반환 + +여기 해당하지 않는 함수는 전부 1차(first order) 함수입니다. [1] + +다른 많은 언어의 구현과는 달리, JavaScript는 "first-class functions"을 가지고 있기 때문에 높은 순위 함수를 이용할 수 있습니다. 이 말은 함수를 JavaScript 안의 다른 값들처럼 사용할 수 있다는 이야기입니다. 문자열이나 숫자처럼, 함수 값은 변수나 객체의 프로퍼티나 다른 함수에 인자로 넘길 수 있습니다. 함수 값은 사실은 객체(`Function.prototype`으로부터 상속됨)입니다. 그래서 거기에 다른 객체와 마찬가지로 프로퍼티를 추가하고 값을 저장할 수 있습니다. + +JavaScript의 다른 형과 함수의 중요한 차이점은 호출 구문입니다. `someFunctionValue(arg1, arg2, etc)`처럼 함수의 참조 뒤에 괄호와 함께 쉼표(,)로 구별되는 값들이 온다면, 함수의 몸통은 (있다면) 주어진 인자로 실행 될 것입니다. + +이 예제에서는 함수를 인자로 넘겨봄으로써 함수는 값처럼 넘길 수 있다는 것을 보여드리겠습니다. + +# 해야할 일 + +첫 번째 인자로 함수, 두 번째 인자로 숫자 `num`을 받아 넘겨진 함수를 `num`번 실행하는 함수를 구현하세요. + +시작할 때 아래의 템플릿을 사용하세요. 앞으로 대부분의 연습 문제는 템플릿이 제공됩니다. + +## 인자 + +* operation: 인자를 받지 않고 유용한 값을 반환하는 함수 +* num: `operation`을 호출할 횟수 + +## 참고 + +* 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 + +## 힌트 + +* 너무 깊게 생각하지 마세요. 생각보다 간단합니다. +* 구현에 재귀를 사용하고 있다면 반복문을 사용하셔도 됩니다. (보너스 포인트) +* 넘겨진 함수에서 하는 출력을 볼 수도 있습니다. +* 아무것도 console.log할 필요 없습니다. + +## 템플릿 + +```js +function repeat(operation, num) { + // 여기에 해답을 적으세요 +} + +// 밑의 줄을 지우지 마세요 +module.exports = repeat +``` diff --git a/exercises/higher_order_functions/problem.md b/exercises/higher_order_functions/problem.md index 1047141..e04e898 100644 --- a/exercises/higher_order_functions/problem.md +++ b/exercises/higher_order_functions/problem.md @@ -7,9 +7,9 @@ All other functions are first order functions. [1] Unlike many other languages with imperative features, JavaScript allows you to utilize higher-order functions because it has "first-class functions". This means functions can be treated just like any other value in JavaScript: just like Strings or Numbers, Function values can be stored as variables, properties on objects or passed to other functions as arguments. Function values are actually Objects (inheriting from `Function.prototype`) so you can even add properties and store values on them, just like any regular Object. -The key difference between Functions and other value types in JavaScript is the call syntax: if a reference to a function is followed by parens and some optional comma-separated values: `someFunctionValue(arg1, arg2, etc)`, then the function body will be executed with the supplied arguments (if any). +The key difference between Functions and other value types in JavaScript is the call syntax: if a reference to a function is followed by parenthesis and some optional comma-separated values: `someFunctionValue(arg1, arg2, etc)`, then the function body will be executed with the supplied arguments (if any). -In this exercise we're going demonstrate that functions can be passed as values by passing you a function as an argument. +In this exercise we're going to demonstrate that functions can be passed as values by passing you a function as an argument. # Task @@ -27,11 +27,10 @@ Use the boilerplate code given to you below to get started. Most/all future exer * 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 -## Hints +## Hints * Don't overthink it, the code should be rather simple. -* It's ok to use a loop in your implementation, bonus points -if you use recursion instead. +* It's ok to use a loop in your implementation, bonus points if you use recursion instead. * You may notice some output. That is coming from the function we passed you. * You do not need to console.log anything. diff --git a/exercises/implement_map_with_reduce/exercise.js b/exercises/implement_map_with_reduce/exercise.js index 4e4a277..148e319 100644 --- a/exercises/implement_map_with_reduce/exercise.js +++ b/exercises/implement_map_with_reduce/exercise.js @@ -10,4 +10,9 @@ var numbers = Array.apply(null, {length: Math.random() * 20 + 1}).map(function() return randomInt(0, 9) }) -module.exports = runner(numbers, function(item) { return item * 3 }) +module.exports = runner.custom(function(map, numbers) { + var result = [] + result.push('non-array entries', map(numbers, function(item) { return item * 3 })) + result.push('array entries', map(numbers, function(item) { return [item * 3] })) + return result +})(numbers) diff --git a/exercises/implement_map_with_reduce/problem.fr.md b/exercises/implement_map_with_reduce/problem.fr.md index 05e1490..3218108 100644 --- a/exercises/implement_map_with_reduce/problem.fr.md +++ b/exercises/implement_map_with_reduce/problem.fr.md @@ -30,7 +30,11 @@ console.log(output) // => [2,4,6,8,10] ## Base de travail ```js -module.exports = function arrayMap(arr, fn) { + +function arrayMap(arr, fn) { // VOTRE SOLUTION ICI } + +module.exports = arrayMap; + ``` diff --git a/exercises/implement_map_with_reduce/problem.ko.md b/exercises/implement_map_with_reduce/problem.ko.md new file mode 100644 index 0000000..03db1eb --- /dev/null +++ b/exercises/implement_map_with_reduce/problem.ko.md @@ -0,0 +1,43 @@ +# 해야할 일 + +Array#reduce를 사용해 단순한 Array#map을 구현하세요. + +## 예상 출력 + +`map` 함수는 배열의 각 요소에 함수를 적용한 결과를 새로운 배열에 수집합니다. + +```js + +var nums = [1,2,3,4,5] + +// `map`은 당신이 export한 함수 +var output = map(nums, function double(item) { + return item * 2 +}) + +console.log(output) // => [2,4,6,8,10] + +``` + +## 인자 + +* input: 어떤 형도 넣을 수 있는 임의의 배열 +* operation: `input` 안의 요소에 적용할 수 있는 임의의 함수 + +## 참고 + +* https://en.wikipedia.org/wiki/Reduce_(higher-order_function) +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce + +## 템플릿 + +```js + +function arrayMap(arr, fn) { + // 여기에 해답을 적으세요 +} + +module.exports = arrayMap; + +``` + diff --git a/exercises/implement_map_with_reduce/problem.md b/exercises/implement_map_with_reduce/problem.md index bbb5a24..015030d 100644 --- a/exercises/implement_map_with_reduce/problem.md +++ b/exercises/implement_map_with_reduce/problem.md @@ -24,6 +24,10 @@ console.log(output) // => [2,4,6,8,10] * input: an arbitrary Array of any type. * operation: an arbitrary Function which can be applied to items in `input`. +## Hints + +* No need to implement the optional `thisArg` argument of `Array.prototype.map`, bonus points if you do! + ## Resources * https://en.wikipedia.org/wiki/Reduce_(higher-order_function) @@ -33,9 +37,11 @@ console.log(output) // => [2,4,6,8,10] ```js -module.exports = function arrayMap(arr, fn) { - // SOLUTION GOES HERE +function arrayMap(arr, fn) { + //SOLUTION GOES HERE } +module.exports = arrayMap; + ``` diff --git a/exercises/implement_map_with_reduce/solution/solution.js b/exercises/implement_map_with_reduce/solution/solution.js index 4c9ca8a..170e47f 100644 --- a/exercises/implement_map_with_reduce/solution/solution.js +++ b/exercises/implement_map_with_reduce/solution/solution.js @@ -1,5 +1,6 @@ -module.exports = function map(arr, fn, thisArg) { +module.exports = function arrayMap(arr, fn, thisArg) { return arr.reduce(function(acc, item, index, arr) { - return acc.concat(fn.call(thisArg, item, index, arr)) + acc.push(fn.call(thisArg, item, index, arr)) + return acc }, []) } diff --git a/exercises/partial_application_with_bind/problem.ko.md b/exercises/partial_application_with_bind/problem.ko.md new file mode 100644 index 0000000..b49b7b2 --- /dev/null +++ b/exercises/partial_application_with_bind/problem.ko.md @@ -0,0 +1,45 @@ +# 해야할 일 + +**Function#bind를 사용해** 메시지에 네임스페이스를 사용할 수 있는 로그 함수를 구현하세요. + +구현은 네임스페이스 문자열을 받아야 하고 콘솔에 네임스페이스가 앞에 붙은 메시지를 출력하는 함수를 반환해야 합니다. + +반환된 로그 함수에 넘겨진 *모든* 인자는 출력되어야 합니다. + +** 결과를 콘솔에 직접 출력하세요 ** + +## 인자 + +* 네임스페이스: 반환하는 함수에 넘겨질 각 메시지의 앞에 붙을 문자열 + +## 예제 + +```js + +var info = logger('INFO:') +info('this is an info message') +// INFO: this is an info message + +var warn = logger('WARN:') +warn('this is a warning message', 'with more info') +// WARN: this is a warning message with more info + +``` + +## 조건 + +* Function#bind를 사용할 것 + +## 템플릿 + +```js + +module.exports = function(namespace) { + // 여기에 해답을 적으세요 +} + +``` + +## 참고 + +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind diff --git a/exercises/partial_application_without_bind/problem.ko.md b/exercises/partial_application_without_bind/problem.ko.md new file mode 100644 index 0000000..891d6c2 --- /dev/null +++ b/exercises/partial_application_without_bind/problem.ko.md @@ -0,0 +1,127 @@ +부분 애플리케이션을 사용해 이미 있는 함수로부터 인자의 개수를 고친 새로운 함수를 만들 수 있습니다. 부분적으로 적용할 인자를 설정한 후, 나머지 인자를 받아 원래 함수를 실행할 수도 있는 새로운 함수를 얻을 수 있습니다. + +더 딱딱하게 말하면 부분 애플리케이션은 더 좁은 목적으로 사용하기 위한 함수를 만들기 위해 함수에 넘길 인자의 수를 고칠 때 참조됩니다. + +예제로, 2개의 인자를 받아 더하는 `add`라는 함수가 있다고 합시다. + +```js + +function add(x, y) { + return x + y +} + +add(10, 20) // => 30 + +``` + +이제, `partiallyApply`라는 함수가 있다고 합시다. `partiallyApply`는 함수와 '부분적으로 사용할' 인자를 받습니다. + +첫 번째 인자를 `add` 함수의 `x`로 '부분적으로 사용했습니다'. + +```js + +var addTen = partiallyApply(add, 10) // `x`를 10으로 고정 + +``` + +새로운 함수 `addTen`은 `add` 함수의 `y` 파라미터를 받습니다. `add`는 아직 호출되지 않았습니다! + +`y`를 위한 인자를 넘기면, 원래 `add` 함수를 실행할 수 있습니다. + +```js + +addTen(20) // => 30 +addTen(100) // => 110 +addTen(0) // => 10 + +// 등등 + +``` + +위에 있는 예제는 모두 `add(10, y)`를 호출하는 것과 같고 `y`는 `addTen`이라고 적절히 이름 지은 호출에 공급됩니다. + +# 해야할 일 + +부분 애플리케이션을 사용해 `console.log`에 넘길 첫 번째 인자를 고정하는 함수를 만드세요. 다시 말해 출력 앞에 네임스페이스를 붙일 수 있는 로그 함수를 구현하세요. + + +구현은 네임스페이스 문자열을 받아야 하고 콘솔에 네임스페이스가 앞에 붙은 메시지를 출력하는 함수를 반환해야 합니다. + +부분 애플리케이션을 구현하기 위해 `Function#apply`를 사용해야 합니다. + +반환되는 로그 함수에서 *모든* 인자가 넘겨지고 출력되어야 합니다. + +** 결과를 콘솔에 직접 출력하세요 ** + +## 인자 + +* 네임스페이스: 반환하는 함수에 넘겨질 각 메시지의 앞에 붙을 문자열 + +## 예제 + +```js + +var info = logger('INFO:') +info('this is an info message') +// INFO: this is an info message + +var warn = logger('WARN:') +warn('this is a warning message', 'with more info') +// WARN: this is a warning message with more info +``` + +## 조건 + +* Function#bind를 사용하지 말 것 +* Function#apply를 사용할 것 + +## 템플릿 + +```js + +var slice = Array.prototype.slice + +function logger(namespace) { + // 여기에 해답을 적으세요 +} + +module.exports = logger + +``` + +## 참고 + +* https://en.wikipedia.org/wiki/Partial_application +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply + +## 힌트 + +`console.log`는 개수와 상관없이 인자를 받을 수 있으며 공백으로 구분해 출력합니다. + +```js + +console.log('hello', 'world') // => 'hello world' +console.log(1,2,3) // => 1 2 3 + +``` + +`console.log`에 넘길 첫 번째 인자를 '부분 적용'하고 싶을 뿐입니다. + +`Function.prototype.apply`를 사용하면 'this의 값'을 새로 제공해 함수를 실행하고 *인자의 배열을 함수에 적용*할 수 있습니다. + + +```js + +add(10, 20) // => 30 +add.apply(null, [10, 20]) // => 30 + +``` + +`apply`를 `Function.prototype.call`과 비교하면 이렇습니다. + +```js + +add.apply(null, [10, 20]) // => 30 +add.call(null, 10, 20) // => 30 + +``` diff --git a/exercises/randomizer.js b/exercises/randomizer.js index 28e53be..513c0dc 100644 --- a/exercises/randomizer.js +++ b/exercises/randomizer.js @@ -33,7 +33,7 @@ function randomWords(count, options) { var result = loremIpsum().split(' ').slice(0, count) if (options.capitalized) { result = result.map(function(word) { - word[0] = word[0].toUpperCase() + word = word[0].toUpperCase() + word.substring(1) return word }) } diff --git a/exercises/recursion/problem.ko.md b/exercises/recursion/problem.ko.md new file mode 100644 index 0000000..01304e5 --- /dev/null +++ b/exercises/recursion/problem.ko.md @@ -0,0 +1,57 @@ +# 해야할 일 + +중복없이 모든 의존성과 모듈의 하위 의존성을 알파벳 순으로 반환하는 재귀 함수를 구현하세요. 의존성은 dependency@version (예: 'inflection@1.2.6')의 형식으로 출력해야 합니다. + +같은 모듈의 여러 버전은 허용됩니다만, 같은 버전이 중복되면 제거해야 합니다. + +## 인자: + +* tree: 의존성 트리. 구조는 아래의 예제를 보세요. + +## 예제 + +```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' ] + +``` + +## 조건: + +* for/while 반복문을 사용하지 마세요. + +## 템플릿 + +```js + +function getDependencies(tree) { + // 여기에 해답을 적으세요 + // 주의: 이 함수를 재귀 호출할 수 있도록 + // 인자를 추가하셔도 됩니다 + // 필요 없을 수도 있습니다! 재귀를 구현하는 방법은 많습니다. +} + +module.exports = getDependencies + +``` + +## 참고 + +* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys diff --git a/exercises/runner.js b/exercises/runner.js index 3222558..6f046c7 100644 --- a/exercises/runner.js +++ b/exercises/runner.js @@ -17,20 +17,18 @@ function runner() { exercise.addProcessor(function(mode, callback) { __ = exercise.__.bind(exercise) + var testFile = this.args[0] try { - submittedFx = require(path.resolve(process.cwd(), this.args[0])); + submittedFx = require(path.resolve(process.cwd(), testFile)); } catch (e) { - var message = (e.code === 'MODULE_NOT_FOUND' - ? __('fail.module_not_found') - : __('fail.missing_deps')) - - this.emit('fail', message) - return callback(null, false) + this.emit('fail', e.stack) + return callback(e, false) } if (typeof submittedFx !== 'function') { - this.emit('fail', __('fail.must_export_function')) - return callback(null, false) + var message = __('fail.must_export_function') + this.emit('fail', message) + return callback(new Error(message), false) } callback(null, true) @@ -85,7 +83,8 @@ function runner() { if (verbose) { console.log(__('solution'), util.inspect(solutionResult, { colors: true }).replace(/,\n\s*/g, ", ")) } - callback(null, deepEqual(submittedResult, solutionResult)) + var resultsMatch = exercise.ignoreReturnValue ? true : deepEqual(submittedResult, solutionResult) + callback(null, resultsMatch) }) if (wrapUpFx) { diff --git a/exercises/trampoline/problem.ko.md b/exercises/trampoline/problem.ko.md new file mode 100644 index 0000000..428cb75 --- /dev/null +++ b/exercises/trampoline/problem.ko.md @@ -0,0 +1,60 @@ +템플릿은 `repeat`의 선언을 포함합니다. `repeat`는 함수 operation, 숫자 num을 받아, operation을 num번 수행합니다. + +```js +var count = 0 +repeat(function() { + count++ +}, 100) + +console.log('executed %d times.', count) +// => executed 100 times. +``` + +하지만 `repeat`를 큰 `num`으로 수행하면 스택 오버플로가 일어날 수 있습니다. + +``` +var count = 0 +repeat(function() { + count++ +}, 100000) + +console.log('executed %d times', count) +// => RangeError: Maximum call stack size exceeded +``` + +# 해야할 일 + +trampoline을 사용해 지속적으로 동기 호출하도록 밑의 템플릿을 수정하세요. + +반복되는 연산이 인자를 사용하지 않고(혹은 이미 기능에 바인딩되었고) 반환 값이 중요하지 않다고 생각해도 됩니다. + +## 조건 + +* 반복문을 포함해 repeat의 구현을 변경하지 마세요. +(그래도 다른 방법으로 변경할 수 있습니다.) + +## 힌트 + +* `repeat`를 수정해 다음 단계가 있다면 '다음 단계'를 반환하도록 하세요. +* trampoline은 다음 단계가 없을 때까지, 계속해서 동기적으로 단계를 실행하고 새로운 단계로 넘어갑니다. 여기에 반복문을 사용할 수 있습니다! +* 프로그램의 수행시간이 길다면, 무언가 잘못된 것입니다. 노드 프로세스를 죽이려면 Control - C를 사용하세요. + +## 템플릿 + +```js +function repeat(operation, num) { + // 스택 오버플로를 일으키지 않도록 수정하세요! + if (num <= 0) return + operation() + return repeat(operation, --num) +} + +function trampoline(fn) { + // trampoline을 구현하셔야 합니다. +} + +module.exports = function(operation, num) { + // 여기서 trampoline을 호출하셔야 합니다! + return repeat(operation, num) +} +``` diff --git a/functional-javascript.js b/functional-javascript.js index df516cb..2c6edfa 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'] + , languages : ['en', 'fr', 'ko'] }) diff --git a/i18n/en.json b/i18n/en.json index 6e1f8ee..56ed21c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -31,7 +31,8 @@ "call_times": "Method called %d times.", "incorrect_return": "Check your function's return value!", "incorrect_this": "Check the function's this! Hint: Function#apply", - "not_all_args": "Check you are passing ALL the arguments! Hint: Function#apply" + "not_all_args": "Check you are passing ALL the arguments! Hint: Function#apply", + "incorrect_count": "Check that your Spy is counting the method invocations correctly!" }, "Trampoline": { "intro": "repeating %d times", diff --git a/i18n/fr.json b/i18n/fr.json index 1c9d769..46ad0b1 100644 --- a/i18n/fr.json +++ b/i18n/fr.json @@ -51,7 +51,8 @@ "call_times": "Méthode appelée %d fois.", "incorrect_return": "Vérifiez la valeur de retour de votre fonction !", "incorrect_this": "Vérifiez le `this` de votre fonction ! Conseil : `Function#apply()`.", - "not_all_args": "Vérifiez que vous transmettez bien TOUS les arguments ! Conseil : `Function#apply()`." + "not_all_args": "Vérifiez que vous transmettez bien TOUS les arguments ! Conseil : `Function#apply()`.", + "incorrect_count": "Vérifiez que votre Spy est compter les appels de méthode correctement!" }, "Trampoline": { "intro": "Répétition de la fonction %d fois", diff --git a/i18n/ko.json b/i18n/ko.json new file mode 100644 index 0000000..bbcd23b --- /dev/null +++ b/i18n/ko.json @@ -0,0 +1,70 @@ +{ + "title": "FUNCTIONAL JAVASCRIPT IS GOOD", + "subtitle": "\u001b[23m연습 문제를 선택하고 \u001b[3m엔터\u001b[23m를 눌러 시작하세요", + "common": { + "exercise": { + "fail": { + "missing_deps": "해결책에서 사용하는 모든 의존성을 설치하셔야 합니다. (예: lodash)", + "module_not_found": "파일을 찾을 수 없습니다. 경로가 정확한지 확인하세요.", + "must_export_function": "항상 module.exports 객체를 사용해 함수를 반환해야 합니다." + }, + "input": "input: %s", + "submission": "submission: %s", + "solution": "solution: %s" + } + }, + "exercise": { + "Hello World": "헬로우 월드", + "Higher Order Functions": "고차 함수", + "Basic: Map": "기초: Map", + "Basic: Filter": "기초: Filter", + "Basic: Every Some": "기초: Every, Some", + "Basic: Reduce": "기초: Reduce", + "Basic: Recursion": "기초: Recursion", + "Basic: Call": "기초: Call", + "Partial Application without Bind": "바인드 없는 부분 애플리케이션", + "Partial Application with Bind": "바인드 있는 부분 애플리케이션", + "Implement Map with Reduce": "Reduce로 Map 구현하기", + "Function Spies": "함수 스파이", + "Blocking Event Loop": "블로킹 이벤트 루프", + "Trampoline": "트램폴린", + "Async Loops": "비동기 반복", + "Recursion": "재귀", + "Currying": "Currying", + "Function Call": "함수 호출" + }, + "exercises": { + "Basic: Map": { + "didnt_use_map": "Array#map을 사용하지 않았습니다.", + "used_map": "야호! Array#map을 사용했습니다." + }, + "Basic: Every Some": { + "found_good_lists": "%d개의 좋은 목록을 발견했습니다!" + }, + "Basic: Call": { + "matched_objects": "Matched %d of %d valid objects from %d total." + }, + "Higher Order Functions": { + "call_log": "함수가 %d번 호출되었습니다." + }, + "Function Spies": { + "call_times": "메소드가 %d번 호출되었습니다.", + "incorrect_return": "함수의 반환값을 확인하세요!", + "incorrect_this": "함수의 this를 확인하세요! 힌트: Function#apply", + "not_all_args": "인자를 모두 넘겼는지 확인하세요! 힌트: Function#apply", + "incorrect_count": "Check that your Spy is counting the method invocations correctly!" + }, + "Trampoline": { + "intro": "%d번 반복 중", + "result": "성공적으로 %d번 실행되었습니다." + }, + "Async Loops": { + "all_loaded": "모두 %d 사용자를 불러왔습니다!", + "bad_result": "expected: \n%s\n but got:\n%s", + "took_too_long": "너무 오래 걸립니다!" + }, + "Currying": { + "five_words": "This,problem,has,been,solved" + } + } +} diff --git a/package.json b/package.json index 901b7c7..253ed12 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "functional-javascript-workshop", - "version": "1.0.1", + "version": "1.0.6", "description": "The basics of functional programming in JavaScript. No libraries required.", "main": "index.js", "bin": { @@ -18,8 +18,8 @@ "dependencies": { "deep-eql": "^0.1.3", "lorem-ipsum": "^1.0.1", - "workshopper": "^2.3.1", - "workshopper-exercise": "^2.3.0" + "workshopper": "^2.6.0", + "workshopper-exercise": "^2.4.0" }, "repository": { "type": "git",