_reduce함수-part2

_reduce

_reduce함수의 형태는 이렇습니다.

1
2
3
_reduce(list, iter, memo){

}

첫번째인자(list) : 배열
두번째인자(iter) : 함수
세번째인자(memo) : 축적된 값

만약 list에 1~3이 존재하고 함수로 그들을 더하는 add함수가 있다고 가정하면 ,

1
2
console.log(
_reduce([1, 2, 3], add, 0));

위의 결과가 6이 나오도록 해주는 것이 _reduce함수입니다.
어떤 함수나 list가 들어와도 reduce함수를 이용하면 재귀적수행을 쉽게 할 수 있습니다.

위의 예시의 동작순서를 설명하겠습니다.

1
memo = add(0,1)

add함수에 memo의 처음값 0과 배열의 시작값 1을 주고
그 결과값을 memo에 줍니다.

1
memo = add(memo, 2)

그 다음, 위에서 축적된 memo값이 다시 add함수의 첫번째 인자로 들어가고,
배열의 두번째 값 2가 add함수의 두번째 인자로 들어갑니다.
이제 memo값은 3이 되었습니다.

1
2
3
4
5
6
7
8
9
memo = add(memo, 3)
return memo;
```
이어서 똑같이 배열의 마지막값인 3까지 최종적으로 더해져서 memo는 6으로 리턴됩니다.


즉,
```js
add(add(add(0,1),2),3);

_reduce함수는 위처럼 재귀적으로 add함수가 실행된 결과를 만들어주는 역할을 합니다.

1
2
3
function _reduce(list, iter, memo){
iter(iter(iter(0,1),2),3);
}

이런식으로 list의 수만큼 재귀적으로 함수를 수행하는 _reduce함수를 구현해봅시다!

앞 포스팅에서 공부했던 _each함수를 사용하면 쉽게 반복되는 작업을 처리할 수 있습니다.

1
2
3
4
5
6
function _each(list, iter){
for(var i =0 ; i < list.length; i++) {
iter(list[i]);
}
return list; //없어도 실행되는데 문제는 없음.
}

each 함수는 list와 iter함수를 인수로 받아서 list의 값 하나하나 함수에 반복적용하는 역할을 합니다.

1
2
3
4
5
6
7
8
9
function _reduce(list, iter, memo){
_each(list, function(val){
memo = iter(memo, val);
});
return memo;
}
console.log(
_reduce([1,2,3], add, 0)
)

결과값은 6이 나올 것입니다.

동작순서는 다음과 같습니다.

  1. _reduce를 호출합니다.
    인자로 1~3까지의 배열, 인자두개를 받아 더하는 add함수, 처음 더해지는 값 0 을 보냅니다.

  2. _each함수가 실행됩니다.
    인자로 1~3까지의 배열, val을 인자로 받는 함수가 보내집니다.
    이 때, val값은 _each로 인해 list의 i번째 요소들이 들어갑니다.

  3. _each함수 수행결과 값 6을 리턴합니다.


    _reduce함수는 두번째인자로 받은 함수를 연속적으로 대신해서 호출해주고
    값을 계속해서 하나의 값으로 축약해 나가는 함수입니다.

    이 함수는 복잡하고 반복적인 로직을 축약할때 아주 유용하게 사용될 수 있습니다.

_reduce함수는 세번째 인자(memo)를 생략하여 사용할 수도 있습니다.
위에서 만약 세번째 인자가 10이라면 16이라는 결과값을 얻게 되겠죠 .

그런데 세번쨰 인자가 없더라도 배열의 첫번째값이 memo가 되어 수행되면 더 좋겠죠.

그러기 위해서 아래처럼 _reduce에 조건을 넣어줍니다.

1
2
3
4
5
6
7
8
9
10
function _reduce(list, iter, memo){
if(arguments.length == 2){
memo = list[0];
list = list.slice(1);
}
_each(list, function(val){
memo = iter(memo, val);
});
return memo;
}

위에서 쓰인 slice 메소드는 slice(없애고싶은 배열개수) 형태를 띕니다.
즉, 우리는 이미 배열의 첫번째를 처음 초기값으로 지정했기 때문에
중복으로 더해지는 것을 막기위해 배열첫번째를 없애 줘야합니다.
그래서 , slice를 이용하는데요.

문제는 slice가 메소드이기 때문에 list가 무조건 Array객체일 때만 가능하다는 점입니다.
arraylike 객체에는 작동하지 않습니다.

아무튼, 이런 문제를 해결할 수 있는 방법은 다음과 같습니다.
새롭게 _rest 라는 함수를 만듭니다.

1
2
3
function _rest(list, num){
return slice.call(list, num || 1);
}

_rest함수는 인자로 list와 list에서 잘라낼 갯수를 의미하는 num을 가지고,
원래 list에서 num만큼 잘라낸 list를 리턴합니다.

만약 num이 없으면 default값으로 1을 리턴합니다.
그럼이제, _rest를 사용해서 _reduce의 문제를 해결해 봅시다!

1
2
3
4
5
6
7
8
9
10
function _reduce(list, iter, memo){
if(arguments.length == 2){
memo = list[0];
list = _rest(list)
}
_each(list, function(val){
memo = iter(memo, val);
});
return memo;
}

이 함수를 활용하면 재밌는 로직을 만드는 데 도움이 된다고 하네요.

_reduce함수에 대한 포스팅을 마치겠습니다!!

Share