JavaScript/함수형 프로그래밍과 JavaScript ES6+

L.map & L.filter

느리지만 꾸준하게 2021. 8. 20. 15:03

지연성을 가진 L.map을 구현해보자.

앞서 가졌던 map을 지연성을 가진 map으로 만들되, 제너레이터 이터레이터 프로토콜을 기반으로 구현해보는 L.map이 될 것이다.

 

L.map은 평가를미루는 성질을 가지고 평가 순서를 조금 달리 조작할 수 있는 준비가 되어있는 이터레이터를 반환하는 제너레이터 함수라고 볼 수 있다. 코드를 구현해보자.

 

단계적으로 구현해보자. L.map은 제너레이터 함수이고

<script>
    L.map = function *() {
        
    }
</script>

이터러블값만 받도록 한다. 그리고 내부에 있는 값을 순회를 하면서 yield를 통해서 해당하는 값을 그대로 꺼내는 것이다.

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

아래와 같은 map함수가 있다 했을 때 L.map을 만들고 결과를 받아서 next를 하면 아래결과가 나온다.

<script>
    L.map = function* (iter) {
        for (const a of iter) yield a;
    };
    var it = L.map([1, 2, 3]);
    log(it.next());
    log(it.next());
    log(it.next());
</script>

//{value: 1, done: false}
//done: false
//value: 1
//[[Prototype]]: Object
 
//{value: 2, done: false}
//done: false
//value: 2
//[[Prototype]]: Object
 
//{value: 3, done: false}
//done: false
//value: 3
//[[Prototype]]: Object

추가로 mapping할 함수를 받아주고 값마다 함수를 적용해준다. 

L.map의 특징은 next를 통해서 평가한 만큼의 값만 얻어올 수 있다. 

<script>
    L.map = function* (f, iter) {
        for (const a of iter) yield f(a);
    };
    var it = L.map(a => a + 10, [1, 2, 3]);
    log(it.next());
    log(it.next());
    log(it.next());
</script>
//{value: 11, done: false}
//done: false
//value: 11
//[[Prototype]]: Object

//{value: 12, done: false}
//done: false
//value: 12
//[[Prototype]]: Object

//{value: 13, done: false}
//done: false
//value: 13
//[[Prototype]]: Object

그래서 spread operator를 통해서는 아래와 같은 결과를 만들 수도 있다. 

[...it]
(3) [11, 12, 13]

L.map 자체에서는 새로운 array를 만들지 않고 값 하나하나 순회하면서 yield를 통해 함수가 적용된 값을 이터레이터에 next를 실행할 때마다 하나씩 전달하게 되고 그러한 준비가 되어있는 이터레이터 객체를 원하는 방법으로 평가할 수 있다. spread operator라든지 next를 사용해서 평가를 할 수 있는것이다.

[...it]
(3) [11, 12, 13]

[it.next().value]
[11]

 

 

지연성을 가진 L.filter를 구현해보자.

L.map과 거의 비슷한데 약간의 코드만 수정하면 된다. 아래와 같은 상태에서

<script>
    L.filter = function* (f, iter) {
        for (const a of iter) yield f(a);
    };
</script>

이터레이터를 모두 순회하면서 yield한 값이 L.filter에 경우에는 f를 적용한 해당하는 값이 아니라 f를 적용해 봤을 때 true 평가되는 값이 떨어지면 그때만 a를 yield하는 식으로 구현을 한다.

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

</script>

이제 L.filter에 값이 있고 a를 받아서 a % 2를 해주고 해당값에 next를 하게되면 아래와 같은 결과가 나온다.

1 3 false 식으로 한번씩 건너뛰게 된다. next를 할 때마다 yield를 하는데 yield가 총 4번이 되는 것이 아니라 

 

원하는 상황에서만 yield가 되기 때문에 그 때만 해당하는 값이 떨어져서 next를 여러번 했을 때 done true가 되기 전까지 해당하는 값을 필터링을 해서 next했을 때 value가 꺼내지도록 구성이 되었다.

<script>
    L.filter = function* (f, iter) {
        for (const a of iter) if (f(a)) yield a;
    };
    var it = L.filter(a => a % 2, [1, 2, 3, 4]);
    log(it.next())
    log(it.next())
    log(it.next())

</script>

 

 

 

 

<출처 : 유인동 함수형 프로그래밍과 JavaScript ES6+>

https://www.inflearn.com/course/functional-es6/dashboard

 

함수형 프로그래밍과 JavaScript ES6+ - 인프런 | 강의

ES6+와 함수형 프로그래밍을 배울 수 있는 강의입니다. 이 강좌에서는 ES6+의 이터러블/이터레이터/제너레이터 프로토콜을 상세히 다루고 응용합니다. 이터러블을 기반으로한 함수형 프로그래밍,

www.inflearn.com