본문 바로가기

Javascript

reduce 를 이용한 함수 pipe() 를 구현해보자 - JS ES6+ 함수형 프로그래밍 - 19

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

 

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

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

www.inflearn.com

이 내용은 이전 게시물과 이어집니다.

 

앞서 작성한 go() 함수에 이어서, pipe() 라는 함수를 만들어 볼것이다.

pipe() 는 go() 함수와 다르게 '함수를 리턴하는 함수' 이다.

go() 함수는 즉시 함수들과 인자를 전달해서 바로 어떤값을 평가하는데 사용한다면,

pipe() 함수는 나열된 함수들합성된 함수이다.

먼저 pipe() 함수가 어떻게 동작해야할지 시뮬레이션 해보자.

  const go = (...argus: any[]) => reduce((value, fn) => fn(value), argus); // 111
  const pipe = () => {};

  // pipe 함수를 사용하면 이런식으로 사용되게 된다.
  // 연속적으로 실행되는 함수를 축약해서 함수로 리턴하는 것이다.
  const fn_1 = pipe(
    (a: number) => a + 1,
    (a: number) => a + 10,
    (a: number) => a + 100
  );

따라서 pipe() 는 함수를 리턴하는 함수다.

  const pipe = () => () => {};
  
  // 따라서 이 함수를 원할때 실행하면 go() 에서 구현한 것과 동일한 값이 나오도록 구현해보자.
  log(fn_1(0)); // 111
  
  go([
    0,
    (a: number) => a + 1,
    (a: number) => a + 10,
    (a: number) => a + 100,
    log,
  ]); // 111

이제 pipe() 를 구현하는 일만 남았다.

  // 결국 이 pipe 함수는 내부적으로 go() 를 사용하는 함수라고 볼수있다.
  // 함수들을 인자로 전달받고, 나중에 실행할 인자를 받는 형태이다.
  const pipe =
    (...functions: ((value: number) => number)[]) =>
    (value: number) =>
      go(value, ...functions);

  const fn_1 = pipe(
    (a: number) => a + 1,
    (a: number) => a + 10,
    (a: number) => a + 100
  );
  
  log(fn_1(0)); // 111

너무 간단하게 pipe() 함수가 구현되어 버렸다.

타입스크립트로 작성하다가 발견한 것인데, 매개변수를 여러개로 받아 rest 문법을 적용하면

타입스크립트에서 이 매개변수 자체배열 ( Array<any> / any[ ] ) 형태로 취급하는것 같다.

매개변수를 배열로 전달하는것이 아닌, 여러개의 매개변수를 ...rest 하기위해 배열의 형태로 취급 '만' 하는 것이고,

실제로는 func( a, b, c, d ) 형태로 쓸수 있는것이다.

 

pipe 함수 에는 go 함수와 다르게 좀더 기능을 추가해보자.

예를 들어 add 함수 (값을 더하는 함수) 가 있다고 했을때, go 함수의 경우 시작하는 첫 인자가 두개여야 할 경우

  // 이런식으로 하면
  go([
    add(0, 1), // 1 로 평가되어진다.
    (a: number) => a + 10,
    (a: number) => a + 100,
    log,
  ]); // 111

같은 형태로 pipe() 를 사용해 구현 하려면

  const fn_2 = pipe(
    (a: number) => a + 10,
    (a: number) => a + 100
  );

  log(fn_2(add(0, 1))); // 111

이와 같이 add 를 해서 전달해야해서 조금 아쉽다.

따라서 첫번째 함수의 경우에는 인자를 두개이상 받아서 전달할수 있도록 구성해보면 좋을것 같다.

  // 이와 같이 사용하고 싶은 것 이다.
  const fn_2 = pipe(
    (val1, val2) => val1 + val2,
    (a: number) => a + 10,
    (a: number) => a + 100
  );

  log(fn_2(0, 1)); // 111

이제 구현해보자

  // fn_2 가 받을 인자를 여러개의 인자로 받도록 하고
  // go 가 시작하는 부분에서 첫번째 함수에 여러개의 인자를 받고, 
  // 그다음 함수들이 실행되도록 하면 된다.
  const pipe =
    (
      // 이런식으로 함수를 하나 인자로 받아오면
      // 첫번째 함수만 꺼내고 나머지 함수들은 뒤에 따라오게 된다.
      fn: (...values: number[]) => number,
      ...functions: ((value: number) => number)[]
    ) =>
    // 반환할 함수에 여러개의 인자를 받고
    (...value: number[]) =>
      // go 를 시작하는 부분을 add(0, 1) 처럼 첫번째 함수의 인자를 펼쳐서 전달 하면 된다.
      go(fn(...value), ...functions);
      
  const fn_2 = pipe(
    (val1, val2) => val1 + val2,
    (a: number) => a + 10,
    (a: number) => a + 100
  );

  log(fn_2(0, 1)); // 111

이로써 첫번째 함수는 여러개의 매개변수로 사용할수있는 pipe() 함수가 되었다.