해당 내용은 인프런 "함수형 프로그래밍과 Javascript ES6+"라는 강의를 보며 개인적으로 정리하는 내용이다
코드가 계산 (Evaluation) 되어 값을 만드는 것
1 + 2
> 3 // 위에 계산이 3으로 평가 된다
[1, 2+3]
> [1, 5] // 위에 계산이 배열인 [1,5]로 평가 된다.
- 값으로 다를 수 있다
- 변수에 담을 수 있다
- 함수의 인자로 사용될 수 있다
- 함수의 결과로 사용할 수 있다
const a = 10;
const add10 = a => a + 10;
const r = add10(a);
console.log(r) // 20
- 함수가 값으로 다뤄질 수 있다
- 조합성과 추상화의 도구
const add5 = a => a+5;
console.log(add5); // a => a+5
console.log(add5(5)); // 10
const f1 = () => () => 1;
console.log(f1()) // () => 1
const f2 = f1();
console.log(f2) // () => 1
console.log(f2()) // 1
함수를 값으로 다루는 함수
const apply1 = f => f(1); // 고차 함수, f는 함수
const add2 = a => a + 2; // 일급 함수
console.log(apply1(add2));
console.log(apply1(a => a -1));
// 고차 함수, f는 함수
const times = (f,n) => {
let i = -1;
while(++i < n) f(i);
}
times(console.log, 3);
times(a => console.log(a+10), 3);
// 고차함수, 클로저를 리턴함
const addMaker = a => b => a+b;
const add10 = addMaker(10);
console.log(add10(5));
console.log(add10(10));
log('Arr -----------');
const arr = [1, 2, 3];
let iter1 = arr[Symbol.iterator]();
for (const a of iter1) log(a);
log('Set -----------');
const set = new Set([1, 2, 3]);
for (const a of set) log(a);
log('Map -----------');
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);
for (const a of map.keys()) log(a);
for (const a of map.values()) log(a);
for (const a of map.entries()) log(a);
console.clear();
const iterable = {
[Symbol.iterator](){
let i = 3;
return {
next() {
return i == 0 ? {done: true} : {value: i--, done: false};
},
[Symbol.iterator]() {
return this;
}
}
}
};
let iterator = iterable[Symbol.iterator]();
iterator.next();
iterator.next();
for (const a of iterator) log(a); // 1
for (const a of document.querySelectorAll('*')) log(a);
const all = document.querySelectorAll('*');
let iter3 = all[Symbol.iterator]();
log(iter3.next());
log(iter3.next());
log(iter3.next());
const array = [1,2,3];
for(let i = 0; i < array.length; i++){
const element = array[i];
console.log(element);
}
const array = [1,2,3]
for(const element of array){
console.log(element);
}
// 위의 for-of 이터레이터 인터페이스로 어떻게 구현되어 있는지 상상코드를 만들어보자
const iterable = [1,2,3];
const iterator = iterable[Symbol.iterator]();
while(true){
const iteratorResult = iterator.next();
if(iteratorResult.done) break;
console.log(iteratorResult.value);
}
// 1
// 2
// 3
1. 기본적인 제네레이터 사용법
function* gen() {
yield 1;
if (false) yield 2;
yield 3;
}
let iter = gen();
log(iter[Symbol.iterator]() == iter);
log(iter.next());
log(iter.next());
log(iter.next());
log(iter.next());
for (const a of gen()) log(a);
console.clear();
위와 같이 조건문을 넣어서 제너레이터는 문장을 통해 값을 만들 수 있다. 즉 제너레이터는 굉장히 다양한 값들을 로직을 만들어가며 순회 시킬 수 있도록 할 수 있는 강력한 도구이다.
2. yield* 활용 예시
// yield* 사용
function* generatorFunction() {
yield* [1,2,3];
}
// yield* 풀어서 보기
function* generatorFunction2() {
for(const element of [1,2,3]) {
yield element;
}
}
const generator = generatorFunction();
for(const element of generator){
console.log(element);
}
// 1
// 2
// 3
계산의 결과값이 필요할 때까지 계산을 늦추는 기법 (필요한 데이터를 필요한 순간에 생성한다.)
제너레이터를 좀 더 이해하기 위해 제너레이터를 통해서 홀수만 순회하며 생성하는 함수를 만들어보자
function* infinity(start = 0) {
let i = start;
while (true) {
yield i++;
}
}
function* limit(iter, limitValue) {
for (const item of iter) {
yield item;
if (item === limitValue) {
return;
}
}
}
function* odds(limitValue) {
for (const item of limit(infinity(1), limitValue)) {
if (item % 2 === 1) {
yield item;
}
}
}
const iter = odds(10);
console.log(iter.next().value);
console.log(iter.next().value);
console.log(iter.next().value);
console.log(iter.next().value);
console.log(iter.next().value);
console.log(iter.next().value);
console.log(iter.next().value);
for (const item of odds(40)) {
console.log(item);
}
Object.prototype.lazyMap = lazyMap;
Object.prototype.take = take;
function* lazyMap(callback) {
for(const el of this){
yield callback(el);
}
}
function take(n = Infinity){
const result = [];
for(const el of this){
result.push(el);
if(result.length === n) break;
}
return result;
}
function callback(element){
console.log("callback called");
return element + 1;
}
const arr = Array(10000).fill(1);
const mappedArr == arr.lazyMap(callback).take(2);
console.log(mappedArr);
// 총 필요한 2번만 순회를 한다.
const arr = Array(10000).fill(1);
const mappedArr == arr.map(callback).take(2);
console.log(mappedArr);
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '핸드폰케이스', price: 15000},
{name: '후드티', price: 30000},
{name: '바지', price: 25000}
];
let names = [];
for (const p of products) {
names.push(p.name);
}
log(names);
let prices = [];
for (const p of products) {
prices.push(p.price);
}
log(prices);
뽑아내고 싶은 데이터를 함수로 전해줌으로써 코드를 간결하게 만들 수 있다. map은 f라는 함수를 인자값으로 받고 있기 때문에 고차함수 개념이 적용된다.
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '핸드폰케이스', price: 15000},
{name: '후드티', price: 30000},
{name: '바지', price: 25000}
];
const map = (f, iter) => {
let res = [];
for (const a of iter) {
res.push(f(a));
}
return res;
};
log(map(p => p.name, products));
log(map(p => p.price, products));
log([1, 2, 3].map(a => a + 1));
log(map(el => el.nodeName, document.querySelectorAll('*')));
// const it = document.querySelectorAll('*')[Symbol.iterator]();
// log(it.next());
// log(it.next());
// log(it.next());
// log(it.next());
// log(it.next());
function* gen() {
yield 2;
if (false) yield 3;
yield 4;
}
log(map(a => a * a, gen()));
let m = new Map();
m.set('a', 10);
m.set('b', 20);
log(new Map(map(([k, a]) => [k, a * 2], m)));
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '핸드폰케이스', price: 15000},
{name: '후드티', price: 30000},
{name: '바지', price: 25000}
];
const filter = (f, iter) => {
let res = [];
for (const a of iter) {
if (f(a)) res.push(a);
}
return res;
};
// let under20000 = [];
// for (const p of products) {
// if (p.price < 20000) under20000.push(p);
// }
// log(...under20000);
log(...filter(p => p.price < 20000, products));
// let over20000 = [];
// for (const p of products) {
// if (p.price >= 20000) over20000.push(p);
// }
// log(...over20000);
log(...filter(p => p.price >= 20000, products));
log(filter(n => n % 2, [1, 2, 3, 4]));
log(filter(n => n % 2, function* () {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
}()));
const products = [
{name: '반팔티', price: 15000},
{name: '긴팔티', price: 20000},
{name: '핸드폰케이스', price: 15000},
{name: '후드티', price: 30000},
{name: '바지', price: 25000}
];
const nums = [1, 2, 3, 4, 5];
let total = 0;
for (const n of nums) {
total = total + n;
}
log(total);
const reduce = (f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const a of iter) {
acc = f(acc, a);
}
return acc;
};
const add = (a, b) => a + b;
// 기본적인 리듀스 함수 사용
log(reduce(add, 0, [1, 2, 3, 4, 5]));
// 15
// 리듀스를 사용하지 않았을 경우
log(add(add(add(add(add(0, 1), 2), 3), 4), 5));
// 15
// acc 값을 안넣엇을 경우
log(reduce(add, [1, 2, 3, 4, 5]));
// 15