go
첫 번째 parameter를 두 번째 parameter로 전달한다.
이를 마지막 parameter까지 반복하는 함수이다.
즉 이전 함수의 실행결과를 다음 함수의 인자로 전달하는 함수이다.
값을 반환한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const reduce = (f, acc, iter) => { if (!iter) { iter = acc[Symbol.iterator](); acc = iter.next().value; } for (const a of iter) { acc = f(acc, a); } return acc; };
const go = (...args) => reduce((a, f) => f(a), args);
go( 0, (a) => a + 1, (a) => a + 10, (a) => a + 100, console.log );
|
고차함수가 한 번에 이해되지 않아서 콘솔로 실행 순서를 추적해보았다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| const reduce = (f, acc, iter) => { if (!iter) { iter = acc[Symbol.iterator](); acc = iter.next().value; } for (const a of iter) { console.log(f, acc, a); acc = f(acc, a); } return acc; };
const go = (...args) => reduce((a, f) => f(a), args);
go( 0, (a) => a + 1, (a) => a + 10, (a) => a + 100, console.log );
|
실행 순서는 다음과 같다.
- reduce에 콜백함수 c
(a,f)=>f(a)
와 인자배열을 전달하는 함수인 go를 만든다. - reduce로 전달된 인자가 2 개밖에 없으므로 args[0]이 acc가 된다. 이 때 args는 콜백함수만 존재하는 이터러블이 된다.
- args[0]은 이미 평가되었으므로 args[1]부터 args를 순회하며 acc에 c에 args 이러터블의 value를 전달한 값을 저장한다. 즉 acc에는
(acc,args[Symbol.iterator]().next().value)=>args[Symbol.iterator]().next().value(a)
가 실행되어 저장된다. - 모든 콜백함수의 실행을 마친 후 acc를 반환한다.
pipe
go와 같은 역할을 하지만 값이 아닌 함수를 반환한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const reduce = (f, acc, iter) => { if (!iter) { iter = acc[Symbol.iterator](); acc = iter.next().value; } for (const a of iter) { acc = f(acc, a); } return acc; };
const go = (...args) => reduce((a, f) => f(a), args);
const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
const f = pipe( (a, b) => a + b, (a) => a + 10, (a) => a + 100 );
console.log(f(0, 1));
|
마찬가지로 콘솔로 실행순서를 추적해보았다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| const reduce = (f, acc, iter) => { if (!iter) { iter = acc[Symbol.iterator](); acc = iter.next().value; } for (const a of iter) { acc = f(acc, a); } return acc; };
const go = (...args) => reduce((a, f) => f(a), args);
const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
const f = pipe( (a, b) => a + b, (a) => a + 10, (a) => a + 100 );
console.log(f);
console.log(f(0, 1));
|
실행순서는 다음과 같다.
- pipe에 함수를 평가한 값을 parameter로 받도록 하기위해 args의 첫 번째 함수를 따로 분리한다.
1 2 3 4
| const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
|
- f에 pipe와 콜백을 전달한다. f엔 다음과 같은 함수객체가 저장된다.
f의 실행컨텍스트엔 pipe에 저장된 첫 번째 함수가 있다.
1
| f = (...as) => go(f(...as), ...fs);
|
- f에 0,1을 전달한다. f(0,1)은 add(0,1)이므로 1로 평가되어 전달된다.
즉 acc의 초기값은 1이 된다.
1
| f = (0,1) => go(f(0,1), ...fs);
|
- go 내부에서 acc를 계산한후 반환한다.
curry
함수를 parameter로 받아서 원하는 개수만큼의 parameter가 들어왔을 때 평가시켜 반환하는 함수이다.
코드의 재사용성을 높여준다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const curry = (f) => (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
const mult = curry((a, b) => a * b); console.log(mult);
console.log(mult(1));
console.log(mult(1)(2));
const mult3 = mult(3); console.log(mult3(10));
console.log(mult3(5));
console.log(mult3(3));
|
go 응용하기
다음의 코드는 아래와 같이 go를 이용해서 다시 작성할 수 있다.
1 2 3 4 5 6 7 8 9
| console.log( reduce( add, map( (p) => p.price, filter((p) => p.price < 20000, products) ) ) );
|
다음 파라미터의 인자로 이전 파라미터의 실행값이 전달되는 go를 이용해서 다시 작성하였다.
1 2 3 4 5 6 7
| go( products, (products) => filter((p) => p.price < 20000, products), (products) => map((p) => p.price, products), (prices) => reduce(add, prices), console.log );
|
filter,reduce,map에 모두 curry를 적용했을 경우엔 다음과 같이 수정할 수 있다.
1 2 3 4 5 6 7 8 9 10
| go( products, filter((p) => p.price < 20000), map((p) => p.price), reduce(add), console.log );
|
함수 조합으로 함수 만들기
pipe를 이용하여 중복을 제거할 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| go( products, filter((p) => p.price < 20000), map((p) => p.price), reduce(add), log );
go( products, filter((p) => p.price < 20000), map((p) => p.price), reduce(add), log );
|
같은부분을 함수로 작성하여 중복을 제거하였다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const total_price = pipe( map((p) => p.price), reduce(add) );
const base_total_price = (predi) => pipe(filter(predi), total_price);
go( products, base_total_price((p) => p.price < 20000), console.log );
go( products, base_total_price((p) => p.price >= 20000), console.log );
|
Ref
함수형 프로그래밍과 JavaScript ES6+