Einführung in die funktionale Programmierung auf fortgeschrittenem Niveau
\n\nFunktionale Programmierung (FP) ist ein Paradigma, das Berechnungen als Ausführung mathematischer Funktionen betrachtet und die Veränderung von Zuständen und veränderlichen Daten vermeidet. In JavaScript, insbesondere in der Node.js-Umgebung, wird FP aufgrund seiner Vorhersagbarkeit, Testbarkeit und Parallelität immer beliebter.
\n\nWenn Sie bereits die Grundlagen der FP beherrschen (reine Funktionen, Funktionen höherer Ordnung, map, filter, reduce), ist es an der Zeit, zur nächsten Stufe überzugehen. In diesem Artikel behandeln wir fortgeschrittene Konzepte: Currying, Komposition, Monaden und die Arbeit mit unveränderlichen Daten.
1. Currying und partielle Anwendung
\n\nWas ist Currying?
\nCurrying ist der Prozess der Umwandlung einer Funktion, die mehrere Argumente akzeptiert, in eine Folge von Funktionen, von denen jede ein Argument akzeptiert. Dies ermöglicht die Erstellung spezialisierter Funktionen auf der Grundlage allgemeiner Funktionen.
\n\n// Normale Funktion\nconst add = (a, b, c) => a + b + c;\nconsole.log(add(1, 2, 3)); // 6\n\n// Curry-Version\nconst curriedAdd = a => b => c => a + b + c;\nconsole.log(curriedAdd(1)(2)(3)); // 6\n\n// Universelle Curry-Funktion\nconst curry = (fn) => {\n const arity = fn.length;\n const resolver = (...args) => {\n if (args.length < arity) {\n return (...nextArgs) => resolver(...args, ...nextArgs);\n }\n return fn(...args);\n };\n return resolver;\n};\n\nconst multiply = (a, b, c) => a * b * c;\nconst curriedMultiply = curry(multiply);\nconsole.log(curriedMultiply(2)(3)(4)); // 24\nconsole.log(curriedMultiply(2, 3)(4)); // 24\n\nPartielle Anwendung
\nPartielle Anwendung ist das Fixieren eines Teils der Argumente einer Funktion, wodurch eine neue Funktion mit weniger Argumenten entsteht. Im Gegensatz zum Currying erfordert die partielle Anwendung keine strenge Reihenfolge der Aufrufe.
\n\nconst partial = (fn, ...presetArgs) => (...laterArgs) => fn(...presetArgs, ...laterArgs);\n\nconst greet = (greeting, name) => `${greeting}, ${name}!`;\nconst sayHello = partial(greet, 'Hello');\nconst sayHi = partial(greet, 'Hi');\n\nconsole.log(sayHello('Alice')); // Hello, Alice!\nconsole.log(sayHi('Bob')); // Hi, Bob!\n\n2. Funktionskomposition
\n\nFortgeschrittene Komposition
\nKomposition ist der Prozess der Kombination mehrerer Funktionen zur Erstellung einer neuen Funktion. In der fortgeschrittenen FP verwenden wir die Komposition von rechts nach links (compose) und von links nach rechts (pipe).
// Einfache Komposition (von rechts nach links)\nconst compose = (...fns) => (value) => fns.reduceRight((acc, fn) => fn(acc), value);\n\n// Komposition von links nach rechts (pipe)\nconst pipe = (...fns) => (value) => fns.reduce((acc, fn) => fn(acc), value);\n\n// Beispiel: Zeichenkettenverarbeitung\nconst trim = (str) => str.trim();\nconst toLowerCase = (str) => str.toLowerCase();\nconst capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);\nconst removeSpaces = (str) => str.replace(/\\s+/g, '-');\n\nconst slugify = compose(capitalize, toLowerCase, trim, removeSpaces);\n// Äquivalent: removeSpaces(trim(toLowerCase(capitalize(" Hello World "))))\n\nconsole.log(slugify(' Hello World ')); // Hello-world\n\nTransducer
\nTransducer sind eine Komposition von Transformationsfunktionen, die mit beliebigen iterierbaren Datenstrukturen arbeiten. Sie ermöglichen die Kombination mehrerer Operationen (z. B. map und filter) in einem einzigen Durchlauf.
const transducer = (mapper, predicate) => (reducer) => (acc, value) => {\n const mapped = mapper(value);\n return predicate(mapped) ? reducer(acc, mapped) : acc;\n};\n\nconst double = (x) => x * 2;\nconst isEven = (x) => x % 2 === 0;\n\nconst processNumbers = transducer(double, isEven);\nconst sumReduc