JavaScript ES6 - 5. 코드를 값으로 다루어 표현력 높이기


유인동님의 함수형 프로그래밍과 JavaScript ES6+ 인프런 강의를 듣고 개인적으로 정리한 내용입니다.
함수형 프로그래밍과 JavaScript ES6 코드를 값으로 다루어 표현력 높이기에 대해서 설명한다.

go 함수 만들기

...args 인자를 받아서 다음 함수 a => a + 1 그다음 a => a + 10 에게 전달을 하여 계속해서 연속적으로 하나의 일을 해야하는 reduce 를 이용해서 특정 함수로 축약해서 하나의 값으로 만들어 간다는 것을 구현해보자.

reduce 관련은 reduce 관련 링크 에서 확인하면 된다.

1
2
3
4
5
6
7
8
const go = (...args) => reduce((a, f) => f(a), args);

go(
0,
a => a + 1,
a => a + 10,
a => a + 100,
console.log)
console
1
> 111

pipe 함수 만들기

pipe 함수는 go 함수와 다르게 함수를 리턴하는 pipe 함수를 구현해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const go = (...args) => reduce((a, f) => f(a), args);
const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);

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

go(
add(0, 1),
a => a + 10,
a => a + 100,
console.log)

const f = pipe(
(a, b) => a + b,
a => a + 10,
a => a + 100);

console.log(f(0, 1));
console
1
2
> 111
> 111

curry 함수 만들기

함수를 값으로 다루면서 받아둔 함수를 내가 원하는 시점에 평가하는 함수 이다. curry라는 함수는 우선 함수를 받아서 함수를 리턴하고, 인자를 받아서 인자가 원하는 갯수만큼 들어왔을때 받아 두었던 함수를 나중에 평가 시키는 함수 이다.

curry 함수 생성

1
2
3
4
5
6
7
8
const curry = f =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);

const mult = curry((a, b) => a * b);
console.log(mult(1)(2));

const mult3 = mult(3);
console.log(mult3(10));
console
1
2
> 2
> 30
  1. (a, ..._) 첫번째 인자와 나머지 인자를 받음.
  2. _.length가 있으면 즉시 함수를 실행, 즉 인자가 2개 이상이면 즉시 실행
  3. 2번이 아니라면 (..._) => f(a, ..._) 다시 실행한 후 인자를(..._) 받아 합쳐서 실행

go+curry를 사용하여 더 읽기 좋은 코드로 만들기

폴더 구조

fx.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const curry = f =>
(a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);

const map = curry((f, iter) => {
let res = [];
for (const a of iter) {
res.push(f(a));
}
return res;
});

const filter = curry((f, iter) => {
let res = [];
for (const a of iter) {
if (f(a)) res.push(a);
}
return res;
});

const reduce = curry((f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter) {
acc = f(acc, a);
}
return acc;
});
index.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '핸드폰케이스', price: 15000},
{name: '후드티', price: 30000},
{name: '바지', price: 25000}
];

const go = (...args) => reduce((a, f) => f(a), args);
const add = (a, b) => a + b;

console.log(
reduce(
add,
map(p => p.price,
filter(p => p.price < 20000, products))));

go(
products,
products => filter(p => p.price < 20000, products),
products => map(p => p.price, products),
prices => reduce(add, prices),
console.log);

go(
products,
filter(p => p.price < 20000),
map(p => p.price),
reduce(add),
console.log);
console
1
2
3
> 30000
> 30000
> 30000

함수 조합으로 함수 만들기

파이프라인으로 만들어진 코드를 쉽게 조합하여 중복을 제거 할 수 있다.

위에서 예제로 사용했던 products 변수와 동일하다.

중복된 코드 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
go(
products,
filter(p => p.price < 20000),
map(p => p.price),
reduce(add),
console.log);

go(
products,
filter(p => p.price >= 20000),
map(p => p.price),
reduce(add),
console.log);
console
1
2
> 30000
> 75000

중복된 코드 제거해서 공통 함수 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const totalPrice = pipe(
map(p => p.price),
reduce(add));

go(
products,
filter(p => p.price < 20000),
total_price,
console.log);

go(
products,
filter(p => p.price >= 20000),
total_price,
console.log);
console
1
2
> 30000
> 75000

고차함수 이용해서 공통 함수 만들기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const totalPrice = pipe(
map(p => p.price),
reduce(add));

const baseTotalPrice = predi => pipe(
filter(predi),
totalPrice);

go(
products,
baseTotalPrice(p => p.price < 20000),
console.log);

go(
products,
baseTotalPrice(p => p.price >= 20000),
console.log);
console
1
2
> 30000
> 75000

코드를 값으로 다루어 표현력 높이기 소스코드

참조