#1 명령형 코드를 함수형으로 변환시키기
우리가 이때까지 배웠던 for, if, while 문과 같은 명령형 문을 앞에서 배웠던 함수형으로 간략하게 변환시켜보자.
먼저, '홀수를 n개 더하는' 명령형 코드를 보자.
function f1(limit, list){
let acc = 0;
for(const a of list){
if(a % 2){
const b = a * a;
acc += b;
if (--limit == 0) break;
}
}
console.log(acc); // 35
}
f1(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
위 코드는 "리스트를 받아와서 홀수인 값만 뽑아서 거기에 제곱을 하는데, 딱 3개만 뽑고 그 뽑은 값들을 더하는 것"이다. 이 내용을 명령형 코드로 짰다가, 기존에 있던 조건을 간단하게 바꿀려고 했을때, 그 바꾸는 방법은 간단하지도 않으면서 복잡할 것이다. 하지만, 이를 함수형 코드로 짠다면, 간단하면서도 코드가 간결해져서 보기에도 편할 것이다.
이제 위 명령형 코드를 함수형 코드로 하나씩 바꿔보자.
[1] 명령형코드를 함수형코드 바꾸기
1. if를 filter로 바꾸기
앞에서 배웠던 L.filter를 if문을 빼고 넣으면 같은 동작을 한다.
function f1(limit, list){
let acc = 0;
for(const a of L.filter(a => a % 2, list)){
const b = a * a;
acc += b;
if (--limit == 0) break;
}
console.log(acc); // 35
}
f1(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
2. 값 변화 후 변수 할당을 map으로 바꾸기
L.map을 선언해서 "const b = a * a;" 코드 대체하기
function f1(limit, list){
let acc = 0;
for(const a of L.map(a => a * a, L.filter(a => a % 2, list))){
acc += a;
if (--limit == 0) break;
}
console.log(acc); // 35
}
f1(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
3. break를 take로 바꾸기
L.take를 통해 바꿔 볼 것인데, take의 동작을 간단하게 예로 보자.
var it = L.take(2, [1, 2, 3]);
it.next(); // {value: 1, done: false}
it.next(); // {value: 2, done: false}
it.next(); // {value: undefined, done: true}
위와 같이 take는 앞의 매개변수 숫자만큼 리스트에 값을 뽑는 역할을 하는데 이를 이용해서 break 문을 대체해볼 것이다.
function f1(limit, list){
let acc = 0;
for(const a of L.take(limit, L.map(a => a * a, L.filter(a => a % 2, list)))){
acc += a;
}
console.log(acc); // 35
}
4. 축약 및 합산을 reduce로 적용하기
어떤 리스트들을 어떤 하나의 값이나, 새로운 값으로 축약하는 함수가 reduce이다. 그래서 reduce를 통해 값을 합산하여 나타낼 것이다.
function f1(limit, list){
console.log( // 35
_.reduce((acc, a) => acc + a,
L.take(limit,
L.map(a => a * a,
L.filter(a => a % 2, list)))));
}
f1(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
위에서 쓴 reduce는 앞에서 배웠던 것 처럼 따로 함수를 선언해서 사용하면 더 보기 간편하다.
const add = (a, b) => a + b;
function f1(limit, list){
console.log( // 35
_.reduce(add,
L.take(limit,
L.map(a => a * a,
L.filter(a => a % 2, list)))));
}
f1(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
이처럼 함수형으로 코드를 짜보면, 기존 명령형 보다 사람이 더 읽기 쉬운 코드로 표현이 된 걸 볼 수 있을 것이다. 이를 앞에서 배운 go를 통해 좀더 편하게 만들고 이전 코드와 비교해보자.
/* 명령형 코드 */
function f1(limit, list){
let acc = 0;
for(const a of list){
if(a % 2){
const b = a * a;
acc += b;
if (--limit == 0) break;
}
}
console.log(acc); // 35
}
f1(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
------------------------------------------------
/* 함수형 코드 */
function f1(limit, list){
_.go(list,
L.filter(a => a % 2),
L.map(a => a * a),
L.take(limit),
_.reduce(add),
console.log); // 35
}
f1(3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
앞서 사용한 명령형 코드보다 훨씬 보기 편하고, 수정하기도 쉬워진 것을 볼 수 있다.
[2] 명령형코드를 함수형코드 바꾸기
5. while을 range로 바꾸기
먼저, 아래와 같은 명령형 코드가 있다고 보자.
function f2(end){
let i = 0;
while(i < end){
console.log(i); // 0 1 2 3 4 5 6 7 8 9
++i;
}
}
f2(10);
위 코드 역시 L.range를 통해 똑같은 역할을 하는 코드를 만들 수 있다.
function f2(end){
_.each(console.log, L.range(end)); // 0 1 2 3 4 5 6 7 8 9
}
f2(10);
만약 위 코드에서 홀수만 출력해야한다고 한다면 명령형은 다음과 같이 구현할 수 있다.
function f2(end){
let i = 0;
while(i < end){
if(i % 2){
console.log(i); // 1 3 5 7 9
}
++i;
}
}
f2(10);
또, 명령형에서는 이를 if문을 쓰지않고 좀 더 영리하게 표현 할 수 있다.
function f2(end){
let i = 1;
while(i < end){
console.log(i); // 1 3 5 7 9
i += 2;
}
}
f2(10);
함수형 프로그래밍에서도 한 번 작성해보자.
function f3(end){
_.each(console.log, L.range(1, end, 2)); // 1 3 5 7 9
}
f3(10);
함수형 프로그래밍에서도 if와 같은 역할을 하는 filter를 쓰지않고 코드를 작성해보았는데, 이처럼 명령형 뿐만 아니라 함수형에서도 유사하게 구현할 수 있는 것을 알아보았다.
함수형 프로그래밍으로 별 만들기
함수형 프로그래밍을 통해 별 만들기 소스를 단계적으로 작성해보자.
_.go(
_.range(1, 6),
_.map(_.range),
console.log);
▲위 처럼 벌써 계단식으로 코드가 유사하게 나왔음을 볼 수 있다.
_.go(
_.range(1, 6),
_.map(_.range),
_.map(_.map(_ => '*')),
console.log);
▲해당 인자를 '*'로 대체해준다.
_.go(
_.range(1, 6),
_.map(_.range),
_.map(_.map(_ => '*')),
_.map(_.reduce((a, b) => `${a}${b}`)),
console.log);
▲reduce를 통해 합쳐준다.
_.go(
_.range(1, 6),
_.map(_.range),
_.map(_.map(_ => '*')),
_.map(_.reduce((a, b) => `${a}${b}`)),
_.reduce((a, b) => `${a}\n${b}`),
console.log);
▲마지막 역시 reduce를 통해 값을 정리해준다.
_.go(
L.range(1, 6),
L.map(L.range),
L.map(L.map(_ => '*')),
L.map(_.reduce((a, b) => `${a}${b}`)),
_.reduce((a, b) => `${a}\n${b}`),
console.log);
▲ 그리고 마지막으로 코드를 지연성있게 정리해준다.
위에서 결과로 나온 코드는 아래처럼도 표현이 가능하다.
_.go(
L.range(1, 6),
L.map(s => go(
L.range(s),
L.map(_ => '*'),
_.reduce((a, b) => `${a}${b}`)
)),
_.reduce((a, b) => `${a}\n${b}`),
console.log);
그리고 좀 더 간결하게 만들기 위해 join 함수를 선언해서 줄여보자.
const join = sep => _.reduce((a, b) => `${a}${sep}${b}`);
_.go(
L.range(1, 6),
L.map(L.range),
L.map(L.map(_ => '*')),
L.map(join('')),
join('\n'),
console.log);
함수형 프로그래밍으로 구구단 코드 만들기
구구단 코드 역시도 앞에서 배웠던 것과 마찬가지로 다음과 같이 쉽게 표현이 가능하다.
_.go(
L.range(2, 10), // 2~9단
L.map(a => _.go(
L.range(1, 10),
L.map(b => `${a}x${b}=${a*b}`),
join('\n')
)),
join('\n\n'),
console.log);
'Web[웹] > ES6+ 함수형 언어' 카테고리의 다른 글
[ES6+: 응용] map과 filter로 하는 안전한 함수 합성 (0) | 2019.10.29 |
---|---|
[ES6+: 응용] reduce함수를 통해보는 명령형 습관을 지우기 (0) | 2019.10.25 |
[JS: ES6+] 비동기: 동시성 프로그래밍 (2) (0) | 2019.10.21 |
[JS: ES6+] 비동기: 동시성 프로그래밍 (1) (0) | 2019.10.17 |
[JS: ES6+] 제너레이터/이터레이터 프로토콜로 구현하는 지연 평가 (2) (0) | 2019.10.14 |