리듀스의 다양한 용도

reduce 배열 방식은 와 함께 소개되는 경우가 많지만, 그만큼 강력한 방식이라 따로 포스팅할 가치가 있다고 느꼈습니다. 도입에 사용된 전통적인 예reduce는 배열에 있는 모든 요소의 합을 계산하는 다음 함수입니다.

const array = [1, 2, 3, 4, 5];
const sum = array.reduce((a, b) => a + b);


이 예에서 이 방법이 배열의 요소를 단일 값으로 줄이는 직관을 개발하기 시작할 수 있으며 많은 경우에 확실히 할 수 있고 실제로 할 수 있습니다. 그러나 값은 JavaScript에서 거의 무엇이든 될 수 있으므로 축소된 결과가 반드시 단일 기본 값이거나 원래 배열보다 더 작을 필요는 없습니다(비교할 크기 개념을 생각해 낼 수 있는 경우).

다음은 reduce가 제공하는 것입니다.

const array = [1, 2, 3, 4, 5];
const INITIAL_VALUE = 0;

const reduceFunction = (accumulator, element) => accumulator + element;

// Without reduce
let accumulator = INITIAL_VALUE;
for (let i = 0; i < array.length; i++) {
  accumulator = reduceFunction(accumulator, array[i])
}

// With reduce
const accumulator = arrray.reduce(reduceFunction, INITIAL_VALUE);


감속기라고도 하는 reduceFunction 는 두 개의 값을 취하고 첫 번째 인수와 동일한 유형의 값을 반환합니다. 이 반환된 값은 다음 반복의 첫 번째 인수로 제공됩니다. 초기 값이 주어지지 않으면 배열의 첫 번째 요소가 초기 값으로 사용됩니다. 배열 프로토타입에 대한 reduce 메서드의 구현은 이를 Foldable의 인스턴스로 만들고 Haskell은 이 함수foldl를 호출합니다(왼쪽에서 접기). reduce 할 수 있는 몇 가지를 살펴봅시다!

지도


reduce를 사용하여 map를 대체할 수 있습니다. 이 접근 방식의 이점은 즉시 명확하지 않지만 향후 변환기를 다룰 때 도움이 될 것입니다.

const array = [1, 2, 3, 4, 5];
const mapFunc = (number) => number * 2;

// With map
const newarray = array.map(mapFunc);

// With reduce
const mapReducer = (func) => (accumulator, element) =>
  [...accumulator, func(element)];
const newarray = array.reduce(mapReducer(mapFunc), []);


필터


reduce를 사용하여 filter를 대체할 수도 있으며 이는 변환기에 대해 이야기할 때도 도움이 됩니다.

const array = [1, 2, 3, 4, 5];
const predicate = (number) => number % 2 === 0;

// With filter
const newarray = array.filter(predicate);

// With reduce
const filterReducer = (predicate) => (accumulator, element) =>
  predicate(element) ? [...accumulator, element] : accumulator;
const newarray = array.reduce(filterReducer(predicate), []);


다양한 골재



배열에서 생성할 수 있다고 생각할 수 있는 거의 모든 것은 reduce 를 사용하여 생성할 수 있습니다. 나는 특히 배열의 상부 삼각 행렬을 생성하는 이 구현을 좋아합니다. reduce 함수는 요소의 인덱스인 선택적 세 번째 인수를 취합니다. (또한 배열 자체인 네 번째 선택적 인수를 취합니다).

// Using nested for loops
const upperTriangle = (arr) => {
  let triangle = [];
  for (let first = 0; first < arr.length; first++) {
    for (let second = first + 1; second < arr.length; second++) {
      triangle.push([arr[first], arr[second]]);
    }
  }
  return triangle;
};

// Using reduce and map
const upperTriangle = (arr) =>
  arr.reduce((triangle, first, i) => {
    const rest = arr.slice(i + 1);
    const pairs = rest.map(second => [first, second]);
    return [triangle, pairs].flat();
  }, []);


기능 구성



당신은 그 권리를 읽었습니다. reduce 로 함수 합성을 구현할 수 있습니다!

const toWords = (string) => string.split(" ");
const count = (array) => array.length;
const wpm = (wordCount) => wordCount * 80;

const speed = (string) =>
  [toWords, count, wpm]
  .reduce((composed, fn) => fn(composed), string);


재귀 함수



재귀 함수를 반복 접근 방식으로 변환할 수 있는 경우 reduce 를 사용하여 구현할 수도 있습니다. 재귀 함수는 의미론적 정의 때문에 자주 사용되지만 reduce를 사용하면 함수 호출 스택을 잠재적으로 채우는 문제가 없으며 제대로 수행되면 선언적 정의를 활성화할 수 있습니다.

const factorial = (number) =>
  number === 0 ? 1 : number * factorial(number - 1);

const factorial = (number) =>
  Array(number)
    .fill(number)
    .reduce((acc, elem, i) => acc * (elem - i));


합과 친구들



처음 시작한 sum 함수를 다시 살펴보겠습니다. 유사한 패턴을 따르는 많은 예가 있음이 밝혀졌습니다.

const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((a, b) => a + b, 0);
const product = numbers.reduce((a, b) => a * b, 1);
const min = numbers.reduce((a, b) => (a < b ? a : b), Infinity);
const max = numbers.reduce((a, b) => (a > b ? a : b), -Infinity);

const booleans = [true, false, false, true];
const any = booleans.reduce((a, b) => a || b, false);
const all = booleans.reduce((a, b) => a && b, true);


이 모든 경우에 초기 값을 생략할 수 있지만 명확성을 위해 포함했습니다. 이러한 모든 감속기는 동일한 유형의 두 요소를 사용하고 동일한 유형의 다른 요소를 반환합니다. 이 속성은 적절한 시작 값(identities로 알려짐)과 결합되어 Monoid의 정의를 형성합니다. 다음 포스트에서 우리는 Monoids와 그것들이 프로그래밍에서 나타나는 다양한 위치에 대해 자세히 살펴볼 것입니다.

이 포스트가 여러분에게 reduce 사용에 대한 더 나은 직관을 주었기를 바랍니다. mapfilter 와 결합하여 더 이상 for 또는 while 루프를 작성하는 경우가 거의 없습니다. 명령형 루프는 특정 횟수만큼 작업을 수행해야 하는 경우에 더 유용하지만 곧 보게 되겠지만 간단한 명령문보다 값의 표현으로 작업하는 것이 좋습니다.

좋은 웹페이지 즐겨찾기