iterator와 for of 의 관계
함수형 프로그래밍 구현
range / 느긋한 range + take
const go = (...list) => reduce((a, f) => f(a), list);
const curry = (f) => (a, ..._) =>
_.length ? f(a, ..._) : (..._) => f(a, ..._);
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;
});
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 range = (l) => {
let i = -1;
let res = [];
while (++i < l) {
res.push(i);
}
return res;
};
range(4);
//L관련
const L = {};
L.range = function* (l) {
let i = -1;
while (++i < l) {
yield i;
}
};
L.map = curry(function* (f, iter) {
for (const a of iter) yield f(a);
});
L.filter = curry(function* (f, iter) {
for (const a of iter) if (f(a)) yield a;
});
const take = curry((l, iter) => {
let res = [];
for (const a of iter) {
res.push(a);
if (res.length == l) return res;
}
return res;
});
const takeAll = take(Infinity);
go(
range(10),
map((n) => n + 10),
filter((n) => n % 2),
take(2),
console.log
);
go(
L.range(10),
L.map((n) => n + 10),
L.filter((n) => n % 2),
take(2),
console.log
);
range , map , filter를 쓴 빠른계산과 L.range, L.map , L.filter를 쓴 느린계산은 진행과정에서도 차이가 난다.
빠른계산은 range -> map -> filter -> take 순으로 배열의 진행을 처리한다.
느린계산은 take -> filter -> map -> range -> map ->filter ->take의 반복을 처리하며 진행한다.
만약 배열이 엄청 크다면 빠른 계산은 모든 배열을 만들고 해당 배열을 처리해야한다.
하지만, 느린 계산은 해당하는 값마다의 처리를 진행해서 배열이 다 만들어지지 않아도 => 즉, 원하는 요구에 만족하면 해당 내용을 먼저 종료 시키는 효율성에서 앞선다.
console.time();
go(
range(10000000),
map((n) => n + 10),
filter((n) => n % 2),
take(2),
console.log
);
console.timeEnd();
console.time();
go(
L.range(10000000),
L.map((n) => n + 10),
L.filter((n) => n % 2),
take(2),
console.log
);
console.timeEnd();
[ 11, 13 ]
default: 1.503s
[ 11, 13 ]
default: 0.228ms
배열이 클 수록 더욱 차이가 크다.
let users = [
{
name: "a",
age: 21,
family: [
{ name: "a1", age: 53 },
{ name: "a2", age: 47 },
{ name: "a3", age: 16 },
{ name: "a4", age: 15 },
],
},
{
name: "b",
age: 21,
family: [
{ name: "b1", age: 58 },
{ name: "b2", age: 51 },
{ name: "b3", age: 19 },
{ name: "b4", age: 22 },
],
},
{
name: "c",
age: 21,
family: [
{ name: "c1", age: 64 },
{ name: "c2", age: 62 },
],
},
{
name: "d",
age: 21,
family: [
{ name: "d1", age: 42 },
{ name: "d2", age: 42 },
{ name: "d3", age: 11 },
{ name: "d4", age: 7 },
],
},
];
const curry = (f) => (a, ..._) =>
_.length ? f(a, ..._) : (..._) => f(a, ..._);
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;
});
const go = (...list) => reduce((a, f) => f(a), list);
const pipe = (...fs) => (a) => go(a, ...fs);
let L = {};
const isIterable = (a) => a && a[Symbol.iterator];
L.flatten = function* (iter) {
for (const a of iter) {
if (isIterable(a)) for (const b of a) yield b;
else yield a;
}
};
L.map = curry(function* (f, iter) {
console.log(iter, "L.map Iter");
for (const a of iter) {
console.log(a, "L.map a");
yield f(a);
}
});
L.map1 = curry(function* (f, iter) {
console.log(iter, "L.map1 Iter");
for (const a of iter) {
console.log(a, "L.map1 a");
yield f(a);
}
});
L.filter = curry(function* (f, iter) {
for (const a of iter)
if (f(a)) {
console.log(a, "L.Filter a");
yield a;
}
});
const filter = curry((f, iter) => {
console.log(iter, "filter Iter");
let res = [];
for (const a of iter) {
if (f(a)) res.push(a);
}
console.log(res, "Filter");
return res;
});
const take = curry((l, iter) => {
let res = [];
for (const a of iter) {
res.push(a);
if (res.length == l) return res;
}
return res;
});
const add = (a, b) => a + b;
go(
users,
L.map((u) => u.family),
L.flatten,
L.filter((u) => u.age < 20),
L.map1((u) => u.age),
take(3),
reduce(add),
console.log
);
go(
users,
L.map((u) => u.family),
L.flatten,
filter((u) => u.age < 20),
L.map1((u) => u.age),
take(3),
reduce(add),
console.log
);
첫번째 go는 L이 연결되어있고
두번째 go는 L 사이에 filter가 있다.
아마 이러지 않을 까 싶다. 즉, filter는 모든 a b c d 를 순회하지만
L.filter는 필요한 부분만 하나씩 필요할때마다 순회하여 찾아내는 것 같다.