본문 바로가기

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() 함수가 되었다.