- Published on
필요할 때, 딱 그만큼만 - L.map과 L.filter
- Authors

- Name
- Nostrss
- Github
- Github

이번에는 map과 filter의 지연 평가 버전인 L.map과 L.filter를 구현해보려고 한다.
map과 L.map
기존 map 함수 복습
먼저 이전에 만들었던 map 함수를 다시 보자.
const map = (f, iter) => {
let res = []
for (const a of iter) {
res.push(f(a))
}
return res
}
이 함수는 모든 요소에 함수 f를 적용해서 새 배열을 만들어 반환한다. 한 번에 다 계산하는 즉시 평가 방식이다.
const result = map((a) => a + 10, [1, 2, 3])
console.log(result) // [11, 12, 13]
L.map 구현
이제 제너레이터를 사용해서 지연 평가 버전을 만들어보자.
const L = {}
L.map = function* (f, iter) {
for (const a of iter) {
yield f(a)
}
}
코드가 거의 똑같다. 다른 점은 딱 두 가지다.
function*으로 제너레이터 함수로 만들었다res.push(f(a))대신yield f(a)로 값을 내보낸다
const iterator = L.map((a) => a + 10, [1, 2, 3])
console.log(iterator) // L.map {<suspended>}
L.map을 호출하면 배열이 아니라 이터레이터가 나온다. 아직 아무 계산도 하지 않았다.
next()로 하나씩 꺼내보기
이터레이터의 next()를 호출하면 그때 비로소 계산이 일어난다. 즉, 순회할 때마다 계산을 한다는 의미다.
const iterator = L.map((a) => a + 10, [1, 2, 3])
console.log(iterator.next()) // { value: 11, done: false }
console.log(iterator.next()) // { value: 12, done: false }
console.log(iterator.next()) // { value: 13, done: false }
console.log(iterator.next()) // { value: undefined, done: true }
next()를 호출할 때마다 하나씩 계산해서 준다. 첫 번째 next()에서 1 + 10 = 11을 계산하고, 두 번째 next()에서 2 + 10 = 12를 계산한다.
실행 흐름 비교
일반
map:- 호출 즉시 모든 요소에 함수를 적용해서 배열을 만든다
- 배열이 완성되면 반환한다
L.map:- 호출 했을 때는 아직 아무것도 하지 않는다 (준비만 함)
next()를 호출할 때마다 하나씩 계산해서 준다
filter와 L.filter
기존 filter 함수 복습
이전에 만들었던 filter 함수도 다시 보자.
const filter = (f, iter) => {
let res = []
for (const a of iter) {
if (f(a)) res.push(a)
}
return res
}
조건 함수 f가 true를 반환하는 요소만 모아서 새 배열을 만들어 반환한다.
const result = filter((a) => a % 2, [1, 2, 3, 4])
console.log(result) // [1, 3]
L.filter 구현
마찬가지로 제너레이터로 바꿔보자.
L.filter = function* (f, iter) {
for (const a of iter) {
if (f(a)) yield a
}
}
역시 거의 똑같다. res.push(a) 대신 yield a를 사용했다.
const iterator = L.filter((a) => a % 2, [1, 2, 3, 4])
console.log(iterator) // L.filter {<suspended>}
역시 호출하면 이터레이터가 나오고, 아직 아무 계산도 하지 않았다.
next()로 하나씩 꺼내보기
const iterator = L.filter((a) => a % 2, [1, 2, 3, 4])
console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next()) // { value: 3, done: false }
console.log(iterator.next()) // { value: undefined, done: true }
next()를 호출할 때마다 조건에 맞는 값을 하나씩 찾아서 준다.
첫 번째 next()에서는 1이 조건 a % 2를 만족하므로 바로 1을 내보낸다. 두 번째 next()에서는 2를 검사하고(조건 불만족, 넘어감), 3을 검사해서(조건 만족) 3을 내보낸다.
실행 흐름 비교
일반
filter:- 모든 요소를 검사해서 조건에 맞는 것만 배열에 모은다
- 배열이 완성되면 반환한다
L.filter:- 아직 아무것도 하지 않는다 (준비만 함)
next()를 호출하면 조건에 맞는 값이 나올 때까지 검사하고, 찾으면 그것 하나만 내보낸다
왜 지연 평가가 중요한가?
지금까지 본 L.map과 L.filter는 단독으로 쓸 때는 그다지 차이가 없어 보인다. 하지만 이들을 조합할 때 진가가 발휘된다.
핵심은 "부르면 그때 계산한다" 는 것이다. 미리 다 만들어두지 않고, 요청이 있을 때만 동작한다는 것이다.
출처
인프런 함수형 프로그래밍과 JavaScript ES6+ 강의를 학습하고 정리한 내용입니다.

