[JS] 함수형 프로그래밍 입문 #1
본 강좌는 유인동님의 함수형 프로그래밍과 JavaScript ES6+ 강좌를 듣고 정리한 것입니다. 개인 학습용으로 유료 강좌 내용 전부가 아닌 유인동님의 깃허브에 공개된 자료를 바탕으로 코드를 요약 정리했습니다. 문제시 자삭하도록 하겠습니다.
함수형 프로그래밍 입문 #1
개념
평가
- 코드가 계산되어 값을 만드는 것
일급 함수
- 값으로 다룰 수 있고
- 변수에 담을 수 있고
- 함수의 인자로 사용될 수 있고
- 함수의 결과로 사용될 수 있다
- 한 마디로 함수가 값으로 다뤄질 수 있다
고차 함수
- 함수를 값으로 다루는 함수
기본 적용
함수를 인자로 받아서 실행하는 함수
함수를 값으로 받고 원하는 인자를 적용하는 함수 - applicative programming
apply
const apply1 = f => f(1);
const add2 = a => a + 2;
log(apply1(add2)); // 3
log(apply1(a => a - 1)); // 0
times
const times = (f, n) => {
let i = -1;
while(++i < n) f(i);
};
times(log, 3) // 0, 1, 2
times(a => log(a + 10), 3);
addMaker
클로저를 리턴하는 함수
const addMaker = a => b => a + b;
const add10 = addMaker(10);
log(add10) // b => a + b, 함수가 리턴된다
log(add10(5)) // 15
이터러블 / 이터레이터 프로토콜
for … of 는 우리가 알던 i++ 방식이 추상화 된 것이 아니다
내부적으로 iterator를 구현하고 있다
이터러블
이터레이터를 리턴하는 [Symbol.iterator]()
를 가진 값
이터레이터
{value , done}
객체를 리턴하는 next()
를 가진 값
이터러블 / 이터레이터 프로토콜
이터러블을 for … of, 전개 연산자 등과 함께 동작하도록 한 규약
for … of 동작 방식
const arr = [1,2,3];
arr[Symbol.iterator]; // f values() { [native code] } 함수가 반환된다
let iterator = arr[Symbol.iterator](); // iterator 함수를 받고
iterator.next(); // {value : 1, done : false}
iterator.next(); // {value : 2, done : false}
iterator.next(); // {value : 3, done : false}
iterator.next(); // {value : undefined, done : true}
iterator.next(); // {value : undefined, done : true}
// done : true가 되면 for ... of 문을 빠져나오게 된다
// arr[Symbol.iterator] = null; 를 선언하면 순회가 되지 않는다
// map, set도 마찬가지다
전개 연산자
const a = [1, 2];
log([...a, ...[3, 4]]); // 1, 2, 3, 4
// arr[Symbol.iterator] = null; 를 선언하면 not iterator 에러가 출력된다
제너레이터 / 이터레이터
제너레이터
이터레이터이자 이터러블을 생성하는 함수
=> 어떠한 값도 순회할 수 있는 이터레이터의 형태로 제너레이터를 통해 조작할 수 있다
function *gen() {
yield 1;
if (false) yield 2;
yield 3;
return 100;
}
let iter = gen();
log(iter[symbol.iterator]() == iter); // true
log(iter.next()) // {value : 1, done : false}
log(iter.next()) // {value : 3, done : false}
log(iter.next()) // {value : 100, done : true}
홀수만 생성하는 제너레이터
step1
function *odds() {
yield 1;
yield 3;
yield 5;
}
step2
// 제너레이터를 활용해 자동화해보자
function *odds(l) {
for (let i=0; i < l; i++) {
if (i % 2) yield i;
}
}
step3
// 더 많은 제너레이터를 함께 사용해보자
// 무한히 i 값이 증가하는 infinity 함수
// const iter = infinity();
// iter.next() 로 무한히 값이 증가된다
// 하지만 내가 next()로 평가할 때까지만 증가된다. 조작할 수 있다는 이야기.
function *infinity(i = 0) {
while(true) yield i++;
}
function *odds(l) {
for (const a of infinity(1)) {
if (a % 2) yield a;
if (i == l) return;
}
}
step4
function *infinity(i = 0) {
while(true) yield i++;
}
function *limit(l, iter) {
for (const a of iter) {
yield a;
if (i == l) return;
}
}
function *odds(l) {
for (const a of limit(l, infinity(1))) {
if (a % 2) yield a;
}
}
for (const a of odds(40)) log(a);
구조분해할당, 전개 연산자, 나머지 연산자와 함께 쓴다면
log(...odds(10)); // 1 3 5 7 9
log([...odds(10), ...odds(20)]) // [1,3,5,7,9,1,3,5,7,9,11,13,15,17,19]
const [head, ...tail] = odd(5);
log(head) // 1
log(tail) // [3, 5]
const [a,b, ...rest] = odd(10);
log(a) // 1
log(b) // 3
log(rest) // [5, 7, 9]
Date:
Tags:
JS