const products = [
{
name:'name1',
price:100,
quantity:1
},
{
name:'name2',
price:210,
quantity:2
},
{
name:'name3',
price:300,
quantity:4
},
{
name:'name4',
price:405,
quantity:8
},
]
const curry = f => (a, ...args) => args.length ? f(a, ...args) : (...args) => f(a, ...args);
const reduce = curry((f, acc, iter) => {
if(!iter){
iter = acc[Symbol.iterator]();
acc = acc.next().value;
}
for(const a of iter){
acc = f(a, acc)
}
return acc;
})
const map = curry((f, iter)=>{
const temp = [];
for(const a of iter){
temp.push(f(a));
}
return temp;
})
const go = (...args) => reduce((f, acc)=>f(acc), args);
위 두 함수를 정의하고 어떻게 사용하는지 자세히 살펴보자.
const log = console.log;
log(
go(
products,
map(p => p.quantity),
reduce((a, b) => a + b)
)
)
우선 go
함수는 인자들(=args)을 받아 reduce 함수(1)에 함수와 args들을 넘겨준다.
함수(1)은 (f,acc)=>f(acc)
이다.
reduce 함수는 2 개의 인자만을 받아들였으므로 args의 첫 인자인 products를 acc로 받아들이게 된다.
그 후 reduce 함수는 계속 진행되어 for...of 문을 순회하게 된다.
for(const a of iter){ acc = f(a, acc); }
위 문에서 어떤 일이 일어나냐면,
go에 넘겨준 args 중 products는 iterator에 의해 진행 되었으므로 for...of
문에 사용될 보조 함수는
args[1]인 map(p => p.quantity)
가 된다.
따라서
for(const a of iter){
acc = f(a, acc);
}
에서
for(const a of iter){
acc = f(map(p=>p.quantity), acc = products)
}
로 변하게 되는 것이다.
위의 f = (f, acc) => f(acc)
이므로 f라는 함수는 인자를 두 개 받는다.
하나는 보조함수를 받고 다른 하나는 acc 값을 받는다.
그러므로 두 개의 인자를 받은 f는 return 값으로 f(acc)를 하게 되는데 실질적으로는
map(p=>p.quantity)
함수에 (curry 함수에 의해 map 함수는 다음 인자가 다 들어올 때 까지 기다리고 있는 상태이다.)acc = products
를 넣게 되므로 map 함수가 작동하게 된다.
acc = map의 결과인 숫자의 배열을 저장하게 될 것이다. (quantity 값을 저장하므로)
그리고 go-reduce의 for 문은 다음 인자인 함수 reduce를 다시 호출하게 되고
for(const a of iter){
acc = f(a,acc); // go 함수에 의해 (f=a, acc) => a(acc);
}
루프는 다시 돌게 된다.
이번에는 a 값이 reduce((a,b)=>a+b)가 되고, acc는 방금 전 map의 결과물인 숫자 배열값이 된다.
이제 reduce 함수에 acc를 인자로 주게 되면 curry로 감싸인 reduce 함수는 조건이 충족되어
reduce 함수를 실행하게 된다.