reduce 구현하면서 이해하기

김성현·2025년 10월 11일

유인동님 강의를 보면서 FP를 공부하는데, 함수형적 사고가 안된다.
특히, reduce처럼 계속해서 반환값에 함수를 적용하는 로직이 어려워서 정리해봐야겠다.

이터러블을 모르신다면 자바스크립트가 순회를 추상화한 방법 을 먼저 읽어보세요!

reduce를 직접 구현해보자

1부터 5까지의 숫자를 더해 15를 출력해보자. for문으로 아래와 같이 구현할 수 있다.

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

for (const num of nums) {
  sum += num;
}

console.log(sum); // 15

1에 2를 더하고, 1 + 2를 더한 값에 3을 더하고, 1 + 2 + 3을 더한 값에 4를 더하고, 1 + 2 + 3 + 4를 더한 값에 5를 더해서 15를 출력하는 로직이다.

일단 reduce라는 함수에 코드를 그대로 옮겨보자.

const reduce = () => {
  const nums = [1, 2, 3, 4, 5];
  let sum = 0;

  for (const num of nums) {
    sum += num;
  }

  console.log(sum); // 15
}

리펙토링

1. 이터러블 받기

일단, nums는 매개변수로 받아야 한다. 배열만 받는 것이 아니라, 이터러블을 받을 수 있는 함수라는 점에서 매개변수 이름을 iterable로 지어보자.

const reduce = (iterable) => {
  let sum = 0;

  for (const num of iterable) {
    sum += num;
  }

  return sum; // console.log 대신 return으로 바꾸자.
}

2. 누적 값 받기

누적값 sum도 매개변수로 빼자. sum을 함수 안에 두면 재사용성이 없어진다. 그리고 이름을 좀 더 범용적으로 acc로 지어주고, num은 a로 바꿔주자.

const reduce = (acc, iterable) => {
  for (const a of iterable) {
    acc += num;
  }

  return acc;
}

3. 누적 함수 받기

지금은 +=로 num을 누적하는 함수이지만, 다른 로직을 적용할 수 있어야 한다.
따라서 더하기 로직을 대체할 수 있는 함수를 fn으로 받자.

const reduce = (fn, acc, iterable) => {
  for (const a of iterable) {
    acc = fn(acc, a);
  }

  return acc;
}

fn은 누적값 acc에 대해 현재 iterable을 돌고 있는 a를 누적하는 함수니까 f(acc, a)로 적어주어야 한다.

4. 초기값 지정 안해도 되도록 만들기

자바스크립트의 내장 함수 reduce는 따로 초기값을 지정하지 않으면, 자동으로 이터러블의 첫번째 원소가 초기값이 된다. 커스텀 reduce도 이렇게 구현할 수 있다.

초기값이 들어오지 않는다면 acc의 인자에 이터러블이 들어오므로, iterable 값의 유무를 판단해서 다음과 같이 설정해주면 된다.

const reduce = (fn, acc, iterable) => {
  if (!iterable) {
    iterable = acc[Symbol.iterator](); // 이터러블을 반환함, 아래의 next 메소드를 쓰기 위해 이렇게 호출
    acc = iterable.next().value;
  }
  
  for (const a of iterable) {
    acc = fn(acc, a);
  }

  return acc;
}

적용하기

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

const sum = reduce((a, b) => a + b, arr); // 15

// 또는
const sum2 = reduce((a, b) => a + b, 0, arr); // 15

이렇게 add 함수를 빼도 좋다.

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

const sum = reduce(add, arr); // 15

// 또는
const sum = reduce(add, 0, arr); // 15

이제 배열 뿐만 아니라 Set, Map, Node List 등 이터러블을 모두 받을 수 있는 Well-formed reduce를 구현했다.

다음 편에는 reduce를 활용해서 go, pipe를 구현해보자.

0개의 댓글