본문 바로가기

Javascript

reduce 함수를 구현해보자 - JS ES6+ 함수형 프로그래밍 - 15

본 시리즈는 인프런 강의 함수형 프로그래밍과 JavaScript ES6+ ( 지식 공유자 : 유인동 님 ) 의 강의를 수강하면서 내용을 제 방식대로 포스팅하는 글입니다.

 

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

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

www.inflearn.com

reduce 는 값을 축약하는 함수이다.

이터러블 값을 하나의 값으로, 다른 값으로 축약해 나가는 함수다.

reduce 는 언제 사용할까?

  // 이 값들을 모두다 더해서 하나의 값으로 만든다 할때
  // reduce 를 사용하게 된다.
  const nums = [1, 2, 3, 4, 5];

  // 간단한 방법으로 이 로직을 구현해보자.
  let total = 0;

  // 특정 값을 순회하며 하나의 값으로 누적해 나갈때
  // 이런 코드를 쓴다.
  for (const n of nums) {
    total = total + n;
  }

  log(total); // 15

이런 일을 할때 reduce 함수가 필요한데, 한번 직접 구현해서 해결해보도록 하자

  // reduce 함수를 이해하기 위해 먼저 외부 인터페이스를 살펴보자
  // 함수가 있다고 가정
  const reduce = (any1 : any, any2: any, any3 : any) => {};

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

  log(reduce(add, 0, [1,2,3,4,5])) // 이 결과가 15가 나오도록 하는 함수이다.

조금더 개념적으로 이해해보자

                   // 초기값으로 시작하고
  log(add(add(add(add(add(0, 1), 2), 3), 4), 5)); // 15
  // 연속적으로, 재귀적으로 받은 보조함수를 실행하면서 하나의 값으로 누적해나간다.

이제 보조함수를 받아 위임하는 함수를 만들면,

  const nums = [1, 2, 3, 4, 5];

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

  // acc : 누적값
  const reduce = (fn: Function, acc: number, iter: Iterable<any>) => {
    for (const a of iter) {
      // 순회하는 행동을 보조함수에게 위임,
      // 위의 코드랑 비교해보면, 더하는 연산을 하는 함수를 전달받아 위임.
      acc = fn(acc, a);
    }

    // 외부 블록에 영향을 주는것이 아닌, 함수의 리턴값으로 반환
    return acc;
  };

  log(reduce(add, 0, nums)); // 15
실제 JS 에선 acc 값이 없이도 작동할수 있도록 되어있는데, 그 부분을 작성해보자
  // 이와같이 초기값이 없을때, JS 는 이터러블의 첫번째 값을 꺼내서
  log(reduce(add, [1, 2, 3, 4, 5]));
  // 아래와 같이 초기값을 생성한다.
  log(reduce(add, 1, [ 2, 3, 4, 5]));
  
  const nums = [1, 2, 3, 4, 5];

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

  const reduce = (
    fn: Function,
    acc: number | Iterable<any>,
    iter?: Iterable<any> | Iterator<any>
  ) => {
    // 초기값이 없을때는, iter 인자에 전달된것이 없을것이다.
    if (!iter) {
      // acc 가 이터레이터 일것이다.
      acc = acc as Iterable<any>;
      // Symbol.iterator 를 실행해서 이터러블 값으로 변환시킨다.
      iter = acc[Symbol.iterator]();
      // acc 값은 이터러블 안의 첫번째 값이 되야하므로, next() 로 하나 꺼내서 할당해준다.
      acc = iter.next().value;
    }

    for (const a of iter as Iterable<any>) {
      acc = fn(acc, a);
    }

    return acc;
  };

  log(reduce(add, nums)); // 15

이렇게 되면, reduce 가 자바스크립트에 필요한 기능은 다 가지고 있게 된다.