Functional JavaScript
96847 ワード
Conception
1-class関数(一級関数)
価格は. 変数に含めることができます. 関数のパラメータとして使用できます. 関数の結果として使用できます. Higher-order関数(高次関数)
高次関数の2種類
1.パラメータとして関数を実行する関数を受信する(callback)
2.関数を作成して返される関数(モジュールを作成して返される関数)
map def usage def usage def usage def
関数 を順次実行 usage def
関数を順番に実行する関数を返します. usage def
不活性評価(遅延評価)を許可する関数. usage def usage def usage def usage def usage def usage def usage def usage def usage def usage def def def usage
promise with reduce, go, pipe
go,pipe関数はreduceに基づいており,reduceを変更するだけでよい. def usage def usage def usage def
C.reduce, C.take
1.def def usage
1-class関数(一級関数)
価格は
高次関数の2種類
1.パラメータとして関数を実行する関数を受信する(callback)
2.関数を作成して返される関数(モジュールを作成して返される関数)
const users = [
{ "age": 16, "name": "Dalton" },
{ "age": 20, "name": "Amal" },
{ "age": 34, "name": "Owen" },
{ "age": 42, "name": "Vincent" },
{ "age": 56, "name": "Kermit" }
]
functionsmap
const map = (f, iter) => {
const res = [];
for (const el of iter) res.push(f(el));
return res;
}
map(u => u.name, users);
filterconst filter = (f, iter) => {
const res = [];
for (const el of iter) if (f(el)) res.push(el);
return res;
}
filter(u => u.age < 42, users);
reduceconst reduce = (f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const el of iter) acc = f(acc, el);
return acc;
}
reduce((total_age, u) => total_age + u.age, 0, users)
reduce((total_age, u) => total_age + u.age, users)
go関数
const go = (...args) => reduce((a, f) => f(a), args);
go(users,
users => filter(u => u.age < 42, users),
users => map(u => u.age, users),
ages => reduce((a, b) => a + b, ages),
console.log);
pipe関数を順番に実行する関数を返します.
const pipe = (f, ...fs) => (...as) => go(f(...as), ...fs);
const totalYoungAge = pipe(users => filter(u => u.age < 20, users),
users => map(u => u.age, users),
ages => reduce((a, b) => a + b, ages),
console.log);
totalYoungAge(users);
curry不活性評価(遅延評価)を許可する関数.
const curry = f => (a, ..._) => _.length ? f(a, ..._) : (..._) => f(a, ..._);
const curry2 = f => function curried(...as) {
return as.length >= f.length ? f(...as) : (...as2) => curried(...as, ...as2);
}
// 1. 기존함수의 커리화
const filter = curry((f, iter) => {
const res = [];
for (const el of iter) if (f(el)) res.push(el);
return res;
});
const map = curry((f, iter) => {
const res = [];
for (const el of iter) res.push(f(el));
return res;
});
const reduce = curry((f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
}
for (const el of iter) acc = f(acc, el);
return acc;
});
// 2. 가독성 증가
go(users,
filter(u => u.age < 42),
map(u => u.age),
reduce((a, b) => a + b),
console.log);
rangeconst range = (length) => {
const res = [];
for (let i = 0; i < length; i++) res.push(i);
return res;
}
range(5);
L.rangeL.range = function *(length) {
for (let i = 0; i < length; i++) yield i;
}
L.range(5);
takeconst take = curry((length, iter) => {
const res = [];
iter = iter[Symbol.iterator]();
for (const a of iter) {
res.push(a);
if (res.length === length) return res;
}
return res;
});
take(5, range(100));
take(5, L.range(Infinity));
go(
L.range(Infinity),
take(5),
reduce((a, b) => a + b)
);
L.mapL.map = curry(function* (f, iter) {
for (const a of iter) {
yield f(a);
}
});
let it = L.map(a => a * a, [1, 2, 3]);
[...it]; // [1, 4, 9]
L.filterL.filter = curry(function* (f, iter) {
for (const a of iter) {
if (f(a)) yield a;
}
});
let it = L.filter(a => a % 2, [1, 2, 3, 4]);
[...it]; // [1, 3]
joinconst join = curry((sep = ",", iter) => reduce((a, b) => `${a}${sep}${b}`, iter));
join(" ", ["Hello", "world", "!"]);
findconst find = curry((f, iter) => go(
iter,
L.filter(f),
take(1),
([a]) => a
));
find(u => u.age < 42, users);
map with L.mapconst map = curry(pipe(L.map, take(Infinity)));
filter with L.filterconst filter = curry(pipe(L.filter, take(Infinity)));
L.flatten// ver.1
L.flatten = function *(iter) => {
for (const a of iter) {
if (a && a[Symbol.iterator]) for (const b of a) yield b;
else yield a;
}
}
// ver.2
L.flatten = function *(iter) => {
for (const a of iter) {
if (a && a[Symbol.iterator]) yield *a;
else yield a;
}
}
L.flatten([[1, 2], 3, 4, [5, 6]]);
flattenconst flatten = pipe(L.flatten, take(Infinity));
flatten([[1, 2], 3, 4, [5, 6]]);
L.deepFlatL.deepFlat = function *f(iter) {
for (const a of iter) {
if (a && a[Symbol.iterator]) yield *f(a);
else yield a;
}
}
L.flatMapL.flatMap = curry(pipe(L.map, L.flatten));
flatMapconst flatMap = curry(pipe(L.map, flatten));
flatMap(map(a => a * a), [[1, 2], [3, 4]]);
// [[1, 4], [9, 16]]
// [1, 4, 9, 16]
flatMap(L.range, [1, 2, 3])
// [[0], [0, 1], [0, 1, 2]]
// [0, 0, 1, 0, 1, 2]
Promise with functional JSpromise with reduce, go, pipe
go,pipe関数はreduceに基づいており,reduceを変更するだけでよい.
// go1: 1번째 인자를 보고, promise 이면 풀어서 전달
const go1 = (a, f) => a instanceof Promise ? a.then(f) : f(a);
const reduce = curry((f, acc, iter) => {
if (!iter) {
iter = acc[Symbol.iterator]();
acc = iter.next().value;
} else {
iter = iter[Symbol.iterator]();
}
return go1(acc, function recur(acc) {
for (const a of iter) {
acc = f(acc, a);
if (acc instanceof Promise) return acc.then(recur);
}
return acc;
});
});
go(
Promise.resolve(1),
(a) => a + 10,
(a) => Promise.resolve(a + 100),
(a) => a + 1000,
log,
);
go(
Promise.resolve(1),
(a) => a + 10,
(a) => Promise.reject("Error!"),
(a) => a + 1000,
log,
).catch(a => log(a));
promise with L.map, map, takeL.map = curry(function *(f, iter) {
for (const a of iter) {
yield go1(a, f);
}
});
// for of 문은 중간에 끊기는 경우 iterator의 return 메소드를 강제로 실행하기 때문에 여기서는 반드시 while문을 사용해야한다.
// https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Generator/return
const take = curry((l, iter) => {
const res = [];
iter = iter[Symbol.iterator]();
return (function recur() {
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
if (a instanceof Promise) {
return a.then((a) => ((res.push(a), res.length === l) ? res : recur()));
}
res.push(a);
if (res.length === l) return res;
}
return res;
})();
});
go([Promise.resolve(1), Promise.resolve(2), Promise.resolve(3)],
L.map(a => a + 10),
take(2),
log);
promise with L.filter, filter, nop, take// Symbol nop 을 이용해 진짜 오류인지 구별
const nop = Symbol("nop");
L.filter = curry(function* (f, iter) {
for (const a of iter) {
const b = go1(a, f);
if (b instanceof Promise)
yield b.then((b) => (b ? a : Promise.reject(nop)));
else if (b) yield a;
}
});
// take 에서 nop 체크로 흘려주기
const take = curry((l, iter) => {
const res = [];
iter = iter[Symbol.iterator]();
return (function recur() {
let cur;
while (!(cur = iter.next()).done) {
const a = cur.value;
if (a instanceof Promise) {
return a
.then((a) => ((res.push(a), res.length === l) ? res : recur()))
.catch((e) => (e === nop ? recur() : Promise.reject()));
}
res.push(a);
if (res.length === l) return res;
}
return res;
})();
});
go(
[1, 2, 3, 4, 5, 6],
L.map((a) => Promise.resolve(a * a)),
L.filter((a) => a % 2),
take(3),
log,
);
reduce with nopconst reduceF = (acc, a, f) => {
return a instanceof Promise ?
a.then(a => f(acc, a), e => e == nop ? acc : Promise.reject(e))
: f(acc, a);
}
const head = iter => go1(take(1, iter), ([h]) => h);
const reduce = curry((f, acc, iter) => {
if (!iter) return reduce(f, head(iter = acc[Symbol.iterator]()), iter);
iter = iter[Symbol.iterator]();
return go1(acc, function recur(acc) {
let cur;
while (!(cur = iter.next()).done) {
acc = reduceF(acc, cur.value, f);
if (acc instanceof Promise) return acc.then(recur);
}
return acc;
});
});
2.usagego(
[1, 2, 3, 4,],
L.map(a => Promise.resolve(a * a)),
L.filter(a => Promise.resolve(a % 2)),
reduce(add),
log);
並列評価C.reduce, C.take
1.def
const C = {} // Concurrency
function noop() {}
const catchNoop = (arr) => (
arr.forEach((a) => (a instanceof Promise ? a.catch(noop) : a)), arr
);
// 전개연산자를 쓴 시점에서 next()가 실행되고 promise들이 실행 됨.
C.reduce = curry((f, acc, iter) => iter ?
reduce(f, catchNoop(...iter)) :
reduce(f, catchNoop(...acc)));
C.take = curry((l, iter) => take(l, catchNoop([...iter])))
C.takeAll = C.take(Infinity)
2.usageconst delay = (a, time) =>
new Promise((resolve) => {
setTimeout(() => resolve(a), time);
});
go(
[1, 2, 3, 4, 5],
L.map((a) => delay(a * a, (6 - a) * 1000)),
L.filter((a) => a % 2),
C.take(2), // 병렬적으로 모두 실행하지만 2개의 결과만 추출
C.reduce(add),
log,
);
C.map, C.filterC.map = curry(pipe(L.map, C.takeAll));
C.filter = curry(pipe(L.filter, C.takeAll));
C.map(a => delay1000(a * a), [1, 2, 3, 4]).then(log)
C.filter(a => delay1000(a % 2, a), [1, 2, 3, 4]).then(log)
Reference
この問題について(Functional JavaScript), 我々は、より多くの情報をここで見つけました https://velog.io/@wpark/Functional-JavaScriptテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol