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

take 함수

느리지만 꾸준하게 2021. 8. 20. 14:48

take함수를 알아보자 take함수는 아래와 같다. 인자를 두개를 받는데 l값(limit)값을 받고 이터러블을 받는다.(이터러블)

받은 이터러블을 순회를 해준다.

 

결과에 푸시를 해주고 결과를 담고있는 length와 limit이 같아지면 더 순회하지 않고 해당하는 번째까지만 담고서 리턴하는 함수이다. 100개의 range를 만들어서 출력을 하면 아래와 같은 결과가 나온다.

<script>
    const take = (l, iter) => {
        let res = [];
        for (const a of iter) {
            res.push(a);
            if (res.length == l) return res;
        }
        return res;
    };
    log(range(100));
</script>

take함수를 통해서 5개만 잘라볼 수 있다. 

    log(take(5, range(100)));

take 함수같은 경우에는 이터러블 프로토콜을 따르고 있고 이터러블 받아서 이터러블 안에 값을 next를 통해 순회하고 꺼내서 push만 하는 단순한 로직을 가지고 있다.

<script>
    const take = (l, iter) => {
        let res = [];
        for (const a of iter) {
            res.push(a);
            if (res.length == l) return res;
        }
        return res;
    };
    log(take(5, range(100)));
</script>

함수를 만들어 놓고 사용한다면 L.range도 사용을 할 수 있다. L.range같이 지연성을 같는 값을 이터레이터로 만들게 되면 위에 있는 전혀 다른 함수가(const 뒤에 있는 것들) 이터러블 프로토콜만 따른다면 다 소통을 할 수 있게 된다.

 

자바스크립트 고유의 프로토콜을 통해서 만들 수 있기 때문에 조합성이 높고 잘 구성할 수 있다.

    log(take(5, range(100)));
    log(take(5, L.range(100)));

아래 두 코드를 보자 첫번째는 100000크기정도의 array를 만들고 5개를 뽑기 때문에 훨씬 비효율적이고

두번째 코드는 최대 100000만큼 값을 뽑을건데 여기서 5개만 뽑고 들어가기 때문에 100000크기만큼의 array를 만들지 않고 5개의 값만 만들기 때문에 훨씬 효율적이다.

    log(take(5, range(100)));
    log(take(5, L.range(100)));

그래서 두 개의 코드에 time을 찍어보면 두번째 코드가 훨씬 효율적인 것을 알 수 있다.

    console.time('');
    log(take(5, range(100)));
    console.timeEnd('');

    console.time('');
    log(take(5, L.range(100)));
    console.timeEnd('');

또 아래의 경우는 무한수열을 표현해도 된다. 어차피 5개의 값만 만들기 때문에 비슷한 속도를 내는데

첫번째 코드는 무한수열을 표현하면 브라우저가 뻗게 될 것이다.

    console.time('');
    log(take(5, range(100000)));
    console.timeEnd('');

    console.time('');
    log(take(5, L.range(Infinity)));
    console.timeEnd('');

다음으로 curry를 적용하고 같은 코드를 아래와 같이 표현할 수도 있다.

    const take = curry((l, iter) => {
        let res = [];
        for (const a of iter) {
            res.push(a);
            if (res.length == l) return res;
        }
        return res;
    });
    
    
    
    console.time('');
    go(
        range(10000),
        take(5),
        log);
    console.timeEnd('');


    console.time('');
    go(
        L.range(10000),
        take(5),
        log);
    console.timeEnd('');

reduce를 하는데 add를 하겠다라고 코드를 작성한다면 훨씬 효율적으로 할 수 있다.

    console.time('');
    go(
        range(10000),
        take(5),
        reduce(add),
        log);
    console.timeEnd('');


    console.time('');
    go(
        L.range(10000),
        take(5),
        reduce(add),
        log);
    console.timeEnd('');

이러한 지연성은 take나 reduce를 만날 때 연산이 시작되게 된다.

제너레이터 이터레이터를 리턴하는 함수들을 실행을 했을 때 해당하는 연산이 안되서 이뤄지지 않고 reduce라던지 안쪽에 있는 배열의 첫번째 값과 두번째 값을 다 꺼내서 연산을 깨서 다 필요로 하는 reduce 함수나

// [10 + 20]
// 10 + 20

 

몇개의 length가 될 지 모르는 array에서 앞에서 두개의 결과만 꺼내서 만들려고 하는 이러한 연산이 필요할 때까지 최대한 연산을 미루다가. 만났을 때 연산을 처음하는 유형의 기법이다

<script>
    const take = curry((l, iter) => {
        let res = [];
        for (const a of iter) {
            res.push(a);
            if (res.length == l) return res;
        }
        return res;
    });


    console.time('');
    go(
        range(10000),
        // take(5),
        reduce(add),
        log);
    console.timeEnd('');

    // [1, 2, 3, 4, 5, 6, ...]
    // ([1, 2]])

    console.time('');
    go(
        L.range(10000),
        // take(5),
        reduce(add),
        log);
    console.timeEnd('');
</script>

 

 

 

 

 

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

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

 

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

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

www.inflearn.com