이전에 map함수를 만들었었는데 아래와 같이 만들었었는데 이와 비슷한 역할을 하는 L.map이 있을 때 지연적으로 동작하지 않는 즉시 모두 값을 평가해서 결과를 만드는 map을 L.map을 통해서 만들 수가 있다.
const map = curry((f, iter) => {
let res = [];
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
res.push(f(a));
}
return res;
});
L.map = curry(function* (f, iter) {
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
yield f(a);
}
});
우선 아래와 같이 range(4)를 한 다음에 해당하는 값들 모두 10을 더한 결과를 만드는 map을 사용한 코드가 있다.
아래와 같은 코드는 L.range를 사용해도 정상동작을 한다.
log(map(a => a + 10, range(4)));
// (4) [10, 11, 12, 13]
log(map(a => a + 10, L.range(4)));
// (4) [10, 11, 12, 13]
map이 Symbol.iterator을 통해서 이터러벌 객체를 이터레이터로 만들어서 사용하고 있기 때문이다.
const map = curry((f, iter) => {
let res = [];
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
res.push(f(a));
}
return res;
});
이제 해당하는 map을 L.map을 통해 아래 map 코드를 간결하게 만들 수가 있다.
const map = curry((f, iter) => {
let res = [];
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
res.push(f(a));
}
return res;
});
해당하는 iter를 통해서 시작하고 L.map을 먼저 해준다. 확인을 해보면 아래와 같이 평가를 할 수 있는 준비가 된 지연된 값이 된다.
const map = curry((f, iter) => go(
iter,
L.map(f),
));
// Generator {<suspended>}
여기서 해당하는 지연된 값을 모두 결과로 만들어서 리턴해주면 원래 map과 동일한 map이 된다.
take라는 함수를 통해서 값을 Infinity로 전달하게 되면 앞에서 만들어지는 map이 lengthn를 얼마를 가지고 있든 모두 가져와서 결과를 만들기 때문에 간단하게 만들어 진다.
const map = curry((f, iter) => go(
iter,
L.map(f),
take(Infinity)
));
// (4) [10, 11, 12, 13]
아래와 같이 해도 된다. 그리고 pipe로 바꿔서 작성해도 동작을 한다.
const map = curry((f, iter) => go(
L.map(f, iter),
take(Infinity)
));
// (4) [10, 11, 12, 13]
const map = curry(pipe(
L.map,
take(Infinity)
));
// 같은 코드
const map = curry(pipe(L.map, take(Infinity)));
// (4) [10, 11, 12, 13]
filter도 같은 방식으로 하면 원래 filter가 아래와 같이 구현되어 있었다면 L.filter에서 비슷한 로직을 이미 가지고 있고 단지 값을 평가하는 것을 미루기만 하기 때문에 마찬가지로 L.filter를 먼저 사용한 다음에 해당하는 값을 take(Infinity)를 통해 모두 결과를 꺼내면
const filter = curry((f, iter) => {
let res = [];
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
if (f(a)) res.push(a);
}
return res;
});
L.filter = curry(function* (f, iter) {
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
if (f(a)) {
yield a;
}
}
});
아래 코드도 똑같이 만들 수 있다.
log(filter(a => a % 2, range(4)))
// [1, 3]
pipe라고 해주고 L.filter를 한 후에 take(Infinity)를 하겠다라고 해주면 같은 결과가 나온다.
const filter = curry(pipe(L.filter, take(Infinity)));
log(filter(a => a % 2, range(4)))
// [1, 3]
간단하게 filter함수와 map함수를 만들었는데 map과 filter에서 take(Infinity)가 코드가 같은 것을 사용하니까
아래와 같이 해준다.
const takeAll = take(Infinity);
const map = curry(pipe(L.map, takeAll));
const filter = curry(pipe(L.filter, takeAll));
L.map과 L.filter도 breakpoint로 찍으면서 확인해 주기 위해서 세세하게 해당하는 코드를 만들었었는데
L.map = curry(function* (f, iter) {
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
yield f(a);
}
});
L.filter = curry(function* (f, iter) {
iter = iter[Symbol.iterator]();
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
if (f(a)) {
yield a;
}
}
});
간단하게 하기 위해서 L.map과 L.filter도 아래와 같이 바꿔준다.
L.map = curry(function* (f, iter) {
for (const a of iter) {
yield f(a)
}
});
L.filter = curry(function* (f, iter) {
for (const a of iter) {
if (f(a)) yield a
}
});
이렇게 간단하게 L.map과 L.filter를 이용하고 pipe와 take, takeAll을 만들어서 간단하게 filter와 map함수를 구현해보았다.
<출처 : 유인동 함수형 프로그래밍과 JavaScript ES6+>
https://www.inflearn.com/course/functional-es6/dashboard
함수형 프로그래밍과 JavaScript ES6+ - 인프런 | 강의
ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,
www.inflearn.com
'JavaScript > 함수형 프로그래밍과 JavaScript ES6+' 카테고리의 다른 글
L.flatMap, flatMap (0) | 2021.08.24 |
---|---|
L.flatten, flatten (0) | 2021.08.24 |
take, find (0) | 2021.08.24 |
Array.prototype.join 보다 다형성이 높은 join 함수 (0) | 2021.08.24 |
결과를 만드는 함수 reduce, take (0) | 2021.08.24 |