프론트엔드/JS

[JavaScript] 제너레이터/이터레이터 프로토콜로 구현하는 지연 평가

턴태 2023. 5. 2. 18:03

이터러블 중심 프로그래밍에서의 지연 평가 (Lazy Evaluation)

지연 평가라는 게으른 평가라고도 하지만, 영리한 평가로도 불리며 혹은 제때 계산법이라고도 칭한다.

 

가장 필요한 때까지 평가를 미루다가 필요한 순간 코드를 평가하면서 값들을 만들어 나가는 기법이다.

 

배열을 미리 만들어 두는 등 사전에 값을 평가해 준비하는 것이 아니라 reduce와 같이 평가가 필요한 함수를 만났을 때, 배열 안에 있는 값들을 순회하면서 값을 평가하는 것을 최소화하고 연산을 줄이는 것에서 장점이 있다.

 

장사를 하는 것에 비유하자면, 미리 음식을 다 만들어 놓았을 때 다 판매하지 못하면 시간과 돈을 버리게 되지만, 밀키트나 냉동식품을 주문이 들어왔을 때 조리하면서 장사를 하면 더욱 효율적으로 매상을 높일 수 있는 것과 비슷하다.

 

기존 자바스크립트에서 지원하지 않는 기능이었지만, ES6로 넘어가면서 제너레이터와 이터러블을 사용할 수 있게 됐고 이를 통해서 이터러블 프로토콜을 사용하는 이터러블 중심 프로그래밍이 가능해졌다.

 

L.map

앞서 만들었던 map을 지연성을 가지게 만들되 제너레이터와 이터레이터 프로토콜로 구현할 수 있다.

평가할 준비가 되어 있는 이터레이터를 반환하는 제너레이터 함수라고 볼 수 있다.

const L = {};

L.map = function *(iter) {
	for (const a of iter) yield a;
};

const it = L.map([1, 2, 3]);
console.log(it.next());
// {value: 1, done: false}
console.log(it.next());
// {value: 2, done: false}
console.log(it.next());
// {value: 3, done: false}

먼저 map 함수를 완성하기 전에 이터레이터를 반환하는지 확인한다.

정상적으로 이터러블한 이터레이터를 반환하므로, 원하는 함수를 배열에 적용하는 로직을 구현해볼 차례이다.

L.map = function *(f, iter) {
	for (const a of iter) yield f(a);
};

const it = L.map(a => a + 10, [1, 2, 3]);

console.log(it.next());
// {value: 11, done: false}
console.log(it.next());
// {value: 21, done: false}
console.log(it.next());
// {value: 31, done: false}

이때, console.log를 하지 않으면 별도의 일이 생기지 않지만, it.next()로 순회를 하기 시작했을 때 값을 평가해 원하는 값을 얻어낼 수 있다. 동일하게 스프레드 연산자를 사용해도 map 함수를 적용시킬 수 있다.

 

L.filter

이번에도 마찬가지로 기존의 filter 함수를 구현하되 지연 평가를 적용하여 필요한 경우에만 값을 평가해 호출하도록 한다.

L.filter = function *(f, iter) {
	for (const a of iter) if (f(a)) yield a;
};

이렇게 제너레이터를 통해 지연 평가를 할 수 있도록 준비한 후 함수를 실행하여 filter를 실행할 수 있다.

const it = L.filter(a => a % 2, [1, 2, 3, 4]);
console.log(it.next());
// {value: 1, done: false}
console.log(it.next());
// {value: 3, done: false}
console.log(it.next());
// {value: undefined, done: true}

지연 평가 굉장히 획기적인 아이디어라고 생각한다. 모든 평가를 다 하는 게 아니라 내가 필요할 때만 그 평가를 진행해 값으로서 추출해낸다는 것이 효율성 측면에서 좋은 방법이었다. next를 통해 순회를 할 때 비로소 값을 평가하는 것이 맨 처음에 배웠을 때는 굳이? 라는 생각이 들었지만, 몇 번 코드를 짜보고 문제도 해결해 보니까 효율성을 극대화 할 수 있어 좋았다. 서버는 객체 지향적으로 작성하곤 하니까 어떤 쓸모가 있을지 모르겠지만, 한 번 쯤 배워두면 새로운 사고를 할 수 있고 자바스크립트에 익숙해질 수 있어 좋다고 본다.

 

출처: 인프런 함수형 프로그래밍과 JavaScript ES6+