Range 함수
range 함수는 숫자 하나를 받고 그 숫자만한 배열을 리턴하는 함수이다.
예를 들어 아래와 같이 로그를 찍으면,
console.log(range(5))
[0, 1, 2, 3, 4]
라는 배열이 반환되는 것을 기대할 수 있다.
위 함수는 절차적으로 아래와 같이 만들어 낼 수 있다.
const range = l => {
let i = -1;
let res = [];
while (++i < l) {
res.push(i);
}
return res;
};
이제 이 함수를 기존에 만들어 두었던 reduce함수를 사용하여 아래와 같이 나타낼 수 있다.
const add = (a, b) => a + b;
const reduce = (f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter) {
acc = f(acc, a);
}
return acc;
};
reduce(add, range(4));
// 6
느긋한 L.range
느긋한 L.range는 기존 range와 유사하지만 다르다.
먼저 코드를 작성하면서 이해해보자면, 아래와 같이 코드를 작성할 수 있다.
const L = {};
L.range = function *(l) {
let i = -1;
while (++i < l) {
yield i;
}
};
const list = L.range(4);
console.log(reduce(add, list));
// 6
느긋한 L.range는 기존 함수를 제너레이터 함수로 바꿔 이터레이터를 반환하게 된다.
위의 range와 L.range는 모두 이터러블을 받는 것으로 같은 결과를 만든다.
그냥 range의 경우 reduce를 실행하기 전에 이미 list라는 변수에 함수를 평가하여 할당되어 인자로 사용이 된다. 즉, list에 값을 할당할 때 즉시 함수가 실행되어 배열이 평가되고 이것이 담겨 있는 상태이다.
L.range는 약간 다르다.
둘의 차이를 확실히 알아보기 위해 내부 과정을 살펴 볼 필요가 있다.
먼저 내부적으로 동작하는 것을 로그로 찍어볼 수 있다.
const range = l => {
let i = -1;
const res = [];
while (++i < l) {
console.log(i, 'range');
res.push(i);
}
return res;
};
const list = range(4);
const L = {};
L.range = function *(l) {
let i = -1;
while (++i < l) {
console.log(i, 'L.range');
yield i;
}
};
const lList = L.range(4);
// 0 'range'
// 1 'range'
// 2 'range'
// 3 'range'
range 함수는 0부터 3까지 돌면서 모두 range를 출력했지만, L.range는 실행되지 않았다.
이터레이터의 내부를 순회할 때마다 하나씩 값이 평가된다. next() 메서드가 실행되기 전까지는 L.range 함수 안에서 어떤 코드도 동작하지 않기 때문에, 내부의 값을 처음 순회할 때 결과가 꺼내진다.
console.log(lList.next().value);
즉, 둘의 차이는 range 함수의 경우 range를 실행했을 때, 이미 모든 부분이 평가가 되어서 값이 만들어지는 것이며, L.range는 함수를 실행했을 때 평가가 이루어지지 않지고, 배열이 아닌 상태로 평가가 되지 않은 상태로 되다가 필요한 시점에 평가가 일어난다는 점에서 차이가 있다.
또한, 기존에 만들었던 range의 경우에는 array를 만들고, 이 array를 이터레이터로 만들고 next를 한 다음 순회를 해서 만들어지게 되지만, L.range의 경우에는 자기 자신을 리턴하여 이 이터레이터를 리턴하여 순회를 하기 때문에 조금 더 효율적이다.
재미있다.. 뭔가 생각이 확장되는 느낌이다. go와 pipe, curry를 배우면서 언어를 섹시하게 활용할 수 있는 방법을 처음 배웠었다. 기존에 내가 사용하던 언어와 조금 다른 듯한 이질감이 들었다. 나쁜 이질감 보다는 새로운 지평을 열 수 있음에 느낄 수 있는 이질감이었다. 확실히 코드를 처음 봤을 때 이것이 어떤 결과를 낳을 것인지 빠르게 와닿기도 했다. 하지만, 동시에 이를 구현하기 위해 충분히 이해해야 하는 과정은 쉽지 않았다. 조금 더 사고하고 조금 더 확실하게 언어에 대해 이해하는 과정이 필요할 것 같다.
출처: 인프런 함수형 프로그래밍과 JavaScript ES6+
'프론트엔드 > JS' 카테고리의 다른 글
[JavaScript] take 함수 (2) | 2023.05.02 |
---|---|
[JavaScript] range와 느긋한 L.range 테스트 (0) | 2023.05.01 |
[JavaScript] 이터러블 객체와 순회 함수 응용 (0) | 2023.04.30 |
[JavaScript] go, curry 함수를 만들어 읽기 좋은 코드 만들기 (0) | 2023.04.29 |
[JavaScript] go, pipe로 읽기 좋은 코드 만들기 (0) | 2023.04.27 |