[functional JS ES6+] 코드를 값으로 다루어 표현력 높이기, go, pipe, curry
작성:    
업데이트:
카테고리: Functional JS
태그: FE Language, Functional JS, JS
본 포스트는 인프런의 함수형 프로그래밍과 JavaScript ES6+ 강의(링크)를 듣고 정리한 내용입니다.
go
go 함수란?
함수 중첩이 심한 경우 가독성을 좋게 하기 위해 사용
- 코드를 값으로 다루어 인자처럼 사용
- 인자의 첫 번째 결과를 다음 함수의 인자로 사용
// args의 첫 요소는 항상 시작'값'이므로 args는 시작값
const go = (...args) => reduce((a, f) => f(a), args);
go(
0,
a => a + 1,
a => a + 10,
a => a + 100,
log); // 111
- 순서별로 값과 함수를 처리하는 값-함수 묶음
go 함수의 활용
// 기존 reduce, map, filter의 중첩
const add = (a, b) => a + b;
log(
reduce(
add,
map(p => p.price,
filter(p => p.price < 20000, products))));
// go 함수의 활용
// 위의 중첩에서 내부에서 외부 순으로 배치
go(
products,
products => filter(p => p.price < 20000, products),
prices => map(p => p.price, products),
prices => reduce(add, prices),
log);
pipe
pipe란?
- 함수들이 나열되어 있는 합성된 함수
- 내부에서 go를 실행하는 함수
- 첫 함수 동작에 필요한 초기값은 나중에 받음.
- go와 비교하면 값이 없는 완전한 함수 묶음
// 함수들 목록을 인자로 받는데, 초기값을 나중에 받고, 이를 go 함수에 넣어 실행
const pipe = (...fs) => (a) => {go(a, ...fs)};
const f = pipe(
a => a + 1,
a => a + 10,
a => a + 100);
log(f(0));
pipe와 첫 함수 예외
pipe의 첫 함수에만 인자를 여러 개 받고 싶은 경우
const pipe = (f, ...fs) => (...as) => {go(f(...as), ...fs)}
const f = pipe(
(a, b) => a + b,
a => a + 10,
...);
- 구조분해할당을 사용해 첫 함수와 첫 값들을 go 함수 인자 전달에서 인자 함수 내부에 넣어준다.
- 이 경우 f(…as) 자체가 초기값이 되는 것
curry
curry란?
- 값으로 받아놓은 함수를 내가 원하는 시점에 평가시키는 함수
- 원하는만큼의 인자를 받았을 때, 이를 저장해 나중에 평가
2개 이상의 인자를 받았을 때 함수를 실행하는 curry 함수 코드
const curry = f =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
- 가. 함수(f)를 받는다.
- 나. _.length가 참, 즉 a를 제외한 인자가 있는지 여부를 확인
- 다. 조건문
- 다.1. a를 제외한 인자가 있다면 받아놓은 함수 f(a, …_)를 즉시 실행
- 다.2. 아니라면 또 인자를 받아 f(a, …_) 실행
- 라. 함수 실행의 결과를 return
curry 함수의 사용
const multi = curry((a, b) => a * b);
log(multi(3)); // (..._) => f(a, ..._)
log(multi(3)(2)); // 6
- 인자가 1개일 때, 출력된 결과를 보자.
- 인자 하나를 더 추가한다면, 받아두었던 함수에게 추가된 인자를 전달해 실행
- multi(3) 자체가 하나의 함수가 되므로 소괄호를 따로 써준다.
- 다음과 같이 함수 결과를 함수로 재선언해 활용할 수 있다.
const multi3 = multi(3);
log(multi3(10)); // 30
log(multi3(20)); // 60
log(multi3(30)); // 90
curry 함수로 go 함수 축약
// 기존 go 함수
go(
products,
products => filter(p => p.price < 20000, products),
prices => map(p => p.price, products),
prices => reduce(add, prices),
log);
// go 함수와 curry의 활용
go(
products,
filter(p => p.price < 20000),
map(p => p.price),
reduce(add),
log);
- 기존 filter, map, reduce에 curry 함수를 씌운 상황
- curry를 씌우면 map(p => p.price, products)는 map(p => p.price)(products)가 됨
- go 함수는 이전 함수 실행 결과 값(iterable)을 다음 함수의 인자로 전달
- 그렇다면 curry에 추가되는 products 등의 iterable 인자는 go 함수에 의해 이전 함수 실행 결과에서 전달
- go 함수 내부에서는 인자를 따로 지정할 필요가 없이 이전 인자 함수에서 배열을 인자로 받으면 된다.
함수 조합
여러 상황에서 중복되는 코드들을 pipe로 합쳐보자
// 20000원 미만의 상품들의 합계 출력
go(
products,
filter(p => p.price < 20000),
map(p => p.price),
reduce(add),
log);
// 20000원 이상의 상품들의 합계 출력
go(
products,
filter(p => p.price >= 20000),
map(p => p.price),
reduce(add),
log);
// 중복 부분 간소화
const total_price = pipe(
map(p => p.price),
reduce(add));
// 함수를 인자로 전달
const base_total_price = predi => pipe(
filter(predi),
total_price);
// 적용
go(
products,
base_total_price(p => p.price < 20000),
log);
go(
products,
base_total_price(p => p.price >= 20000),
log);
댓글남기기