Dies ist der zweite Teil unserer Artikelserie, die sich mit den Möglichkeiten der funktionalen Programmierung in JavaScript beschäftigt. Lesen Sie diesen Artikel, um Ihr Wissen über Kombinatoren zu erweitern.
Einführung in die Kombinatoren
Kombinatoren sind Funktionen auf höherer Ebene, mit denen Sie Funktionen, Variablen oder andere Kombinatoren kombinieren können. Normalerweise enthalten sie keine Deklarationen ihrer eigenen Variablen oder Geschäftslogik. Sie sind die einzigen, die die Steuerung in einem Funktionsprogramm spülen.
In diesem Artikel werde ich versuchen, einige von ihnen zu behandeln:
- Tippen Sie auf .
- Currying
- Pfeifen/Komponieren
- Gabel
- Abwechslung
- Sequenz
Tippen Sie auf .
Ein Kombinator ist sehr nützlich für Funktionen, die nichts zurückgeben. Er nimmt die Funktion, an die der Parameter geht, und gibt sie dann zurück.
Erklärung
const tap = (fn) => (Wert) => {
fn(Wert);
return value;
};
Imperatives Beispiel
const [items, setItems] = useState(() => [])
axios
.get('http://localhost')
.then({ Daten } => {
setItems(daten)
console.log(daten)
onLoadData(daten)
}).then(...) // gibt undefiniert zurück - die Daten im Versprechen wurden geändert
Deklaratives Beispiel
const [items, setItems] = useState(() => [])
axios
.get('http://localhost')
.then(({ Daten }) => Daten)
.then(tap(setItems)) // (data) => { setItems(data); return data }
.then(tap(console.log)) // eine dann = eine Funktion = eine Verantwortung
.then(tap(onLoadData))
.then(...) // weiterhin Zugriff auf Originaldaten
// einfaches Öffnen/Schließen-Prinzip beibehalten
Currying
Sie teilt die Argumente einer Funktion auf und ermöglicht es, sie nacheinander aufzurufen.
Erklärung
const curry = (fn) => {
const curried = (...args) => {
if (fn.length !== args.length){
return curried.bind(null, ...args)
}
return fn(...args);
};
return curried
};
Beispiel
const curry = (fn) => {
const curried = (...args) => {
if (fn.length !== args.length){
return curried.bind(null, ...args)
}
return fn(...args);
};
return curried
};
const sum = (a, b, c) => a + b + c
const currySum = curry(sum)
/*
mögliche Aufrufe
currySum(a)(b)(c),
currySum(a)(b,c),
currySum(a,b)(c),
currySum(a,b,c)
*/
currySum(1) // (b, c) => 1 + a + b oder (b) => (c) => 1 + a + b
currySum(5)(10) // (c) => 5 + 10 + b
currySum(5, 10) // (c) => 5 + 10 + b
currySum(5)(10)(20) // 35
currySumme(5, 10)(20) // 35
currySumme(5)(10, 20) // 35
const divideBy = curry((a, b) => b / a)
const multiplyBy = curry((a, b) => a * b)
const divideByTwo = divideBy(2)
divideByTwo(10) // ergibt 5
const multiplyByFive = multiplyBy(5)
multiplyByFive(10) // ergibt 50
Pfeifen/Komponieren
Durch Komposition ist es möglich, Daten von einer Funktion an eine andere zu übergeben. Dabei ist es wichtig, dass die Funktionen die gleiche Anzahl von Argumenten annehmen. Der Unterschied zwischen pipe und compose besteht darin, dass erstere die Funktion vom Anfang bis zum Ende ausführt, während compose sie vom Ende her aufruft.
Erklärung
const pipe = (...fns) => (Wert, ...args) =>
fns.reduce((v, f, i) =>
i === 0
? f(v, ...args)
: f(v),
value);
const compose = (...fns) => (value, ...args) =>
fns.reduceRight((v, f, i) =>
i === (fns.length - 1)
? f(v, ...args)
: f(v),
value);
const pipe = (...fns) => (Wert, ...args) =>
fns.reduce((v, f, i) =>
i === 0
? f(v, ...args)
: f(v),
value);
const compose = (...fns) => (value, ...args) =>
fns.reduceRight((v, f, i) =>
i === (fns.length - 1)
? f(v, ...args)
: f(v),
value);
Imperatives Beispiel
const sine = (val) => Math.sin(val * Math.PI / 180) // nicht lesbar
sine(90) // liefert 1
Deklaratives Beispiel
const sine = pipe(
multiplyBy(Math.PI) // ↓ val * Math.PI
divideBy(180), // ↓ val * Math.PI / 180
Math.sin, // ↓ Math.sin(val * Math.PI / 180)
)
const sine = compose(
Math.sin, // ↑ Math.sin(val * Math.PI / 180)
divideBy(180), // ↑ val * Math.PI / 180
multiplyBy(Math.PI) // ↑ val * Math.PI
)
Sinus(90) // ergibt 1
Gabel
Der Fork Combiner ist in Situationen nützlich, in denen Sie einen Wert auf zwei Arten verarbeiten und das Ergebnis kombinieren möchten.
Erklärung
const fork = (join, fn1, fn2) => (Wert) => join(fn1(Wert), fn2(Wert));
Beispiel
const length = (array) => array.length
const sum = (array) => array.reduce((a, b) => a + b, 0)
const divide = (a, b) => a / b
const arithmeticAverage = fork(divide, sum, length)
arithmetischerDurchschnitt([5, 3, 2, 8, 4, 2]) // ergibt 4
Abwechslung
Dieser Kombinator nimmt zwei Funktionen und gibt das Ergebnis der ersten zurück, wenn es wahr ist. Andernfalls gibt er das Ergebnis der zweiten Funktion zurück.
Erklärung
const alt = (fn, orFn) => (Wert) => fn(Wert) || orFn(Wert)
Beispiel
const users = [{
uuid: '123e4567-e89b-12d3-a456-426655440000',
name: 'William'
}]
const findUser = ({ uuid: searchesUuid }) =>
users.find(({ uuid }) => uuid === searchesUuid)
const newUser = data => ({ ...data, uuid: uuid() // neue uuid erstellen })
const findOrCreate = alt(findUser, newUser)
findOrCreate({ uuid: '123e4567-e89b-12d3-a456-426655440000' }) // liefert William-Objekt
findOrCreate({ name: 'John' }) // gibt John-Objekt mit neuer uuid zurück
Sequenz
Es akzeptiert viele unabhängige Funktionen und übergibt an jede von ihnen denselben Parameter. In der Regel geben diese Funktionen keinen Wert zurück.
Erklärung
const seq = (...fns) => (val) => fns.forEach(fn => fn(val))
Beispiel
const appendUser = (id) => ({ Name }) => {
document.getElementById(id).innerHTML = name
}
const printUserContact = pipe(
findOrCreate, // gibt Benutzer zurück
seq(
appendUser('#contact'), // Benutzer => ungültig
console.log, // Benutzer => ungültig
onContactUpdate // benutzer => void
)
)
printUserContact(userData)