본문 바로가기
javascript/함수형프로그래밍과 ES6

range와 느긋한 L.range

by 윤-찬미 2021. 10. 26.

함수형프로그래밍이 아직도 아직도 어렵지만, 재밌는 개념들이 많은 것 같습니다.

(유인동 개발자님 감사합니다...)

이번에는

숫자를 받고, 그숫자만큼의 배열을 리턴하는 range 함수를 만들어 보겠습니다.

const range = l => {
    let i = 0;
  const res = [];
    while (i++ < l) {
    res.push(i)
  }
  return res;
}

// range(5) => [1, 2, 3, 4, 5]

이번엔 해당 배열의 모든 값을 더해보겠습니다.

  • 이때 reduce를 사용할건데, 기존 js Array.prototpe.reduce 말고 이터레이터를 순회하는 reduce를 만들어 사용하겠습니다.
const reduce = (fn, acc, iter) => {
// reduce (add, [1, 2, 3, 4, 5]) 넣어도 reduce (add, 1, [2, 3, 4, 5])로 변환
    if (!iter) {
    iter = acc[Symbol.iterator](); // 이터레이터로 만들고
        acc = iter.next().value; // 첫번째 값 빼기
  }
    for (const a of iter) {
    acc = fn(acc, a); // acc값 갱신
  }
    return acc;

}

const add = (a, b) => a+b;

const list = range(5); // [1, 2, 3, 4, 5]
reduce(add, list); // 15

우리가 원하는 대로 잘 동작이 되는군요!

이번엔 느긋한 range를 만들어보겠습니다 (지연평가를 말합니다)

const L = {};
L.range = function *(l) {
    let i = 0;
    while (i++ < l) {
    yield i;
  }
}

const add = (a, b) => a+b;
const list = L.range(5); // > L.range {<suspended>}
reduce(add, list); // 15

이번에는 range를 제너레이터로 만들었습니다.

과연 어떤 차이가 있을까요?

결론 부터 말하면,

range의 경우 배열을 다 만들고 순회를 하는 반면,

L.range의 경우 이터레이터를 순회하고 있습니다. (yield 할때 하나씩 꺼내옴, 배열을 만들지 않고 reduce로 들어가서 순회를 시작할때값이 평가됨)

미미할 수도 있지만 이는 효율성의 이점도 가져올 수 있습니다.

테스트를 해보겠습니다.

function timeTest(name, time, f) {
    console.time(name);
    while(time --) f();
  console.timeEnd(name);
}

timeTest('range', 10, () => reduce(add, range(100000)));
timeTest('L.range', 10, () => reduce(add, L.range(100000)));

// range: 108.013916015625 ms
// L.range: 31.65576171875 ms

range보다 L.range가 3배보다 조금 더 빠른걸 알 수 있습니다.