Konsole & …: Lambda-Ausdrücke

the next big thing  –  2 Kommentare

„Konsole & Kontext ist eine gemeinsame Serie von Golo Roden und Michael Wiedeking, in der sich die beiden regelmäßig mit Konzepten der (funktionalen) Programmierung beschäftigen. Während Golo Roden in Konsole & … die praktische Anwendung dieser Konzepte in JavaScript und Node.js zeigt, erläutert Michael Wiedeking in ... & Kontext deren jeweiligen eher theoretischen Hintergrund.

Gemäß Wikipedia ist ein Lambda-Ausdruck nichts anderes als eine unbenannte Funktion, die man ausschließlich über eine Referenz aufrufen kann. Ein Lambda-Ausdruck ist daher letztlich bloß eine alternative Bezeichnung für eine anonyme Funktion. Indem man eine anonyme Funktion verwendet, kann man die benannte Funktion

f(x) = x * x

durch die funktional zwar äquivalente, jedoch anonyme Variante

x => x * x

ersetzen. Bemerkenswert ist, dass nicht alle Programmiersprachen dieses Konzept kennen oder gar gleichwertig unterstützen.

Funktionen höherer Ordnung

In JavaScript sind anonyme Funktionen jedoch Bürger erster Klasse, die man in der Regel mit Funktionsausdrücken implementiert. Die zuvor genannte anonyme Funktion kann man auf diesem Weg als

function (x) {
return x * x;
}

definieren. Allerdings ist eine derartig definierte anonyme Funktion nutzlos, da man sie mangels Referenz nicht aufrufen kann. Um eine solche zu definieren, genügt es, den Funktionsausdruck einer Variablen zuzuweisen:

var square = function (x) {
return x * x;
};

Funktionsausdrücke kann man in JavaScript auf vielfältige Weise verwenden: Sie ermöglichen es unter anderem, Funktionen auf die gleiche Art wie Daten an andere Funktionen zu übergeben und sie als Rückgabewert zu verwenden. Auf diesem Weg kann man in JavaScript Funktionen höherer Ordnung definieren.

JavaScript und auch Node.js machen hiervon häufig Gebrauch, beispielsweise in Verbindung mit Rückruffunktionen und asynchroner Programmierung. Das folgende Codeschnipsel zeigt dies anhand des Ladens der Datei /etc/passwd:

var file = '/etc/passwd';
fs.readFile(file, { encoding: 'utf8' }, function (err, data) {
// ...
}
);

Funktionskomposition

Darüber hinaus sind Funktionen höherer Ordnung ausgesprochen hilfreich, um mehrere Funktionen zu einer gemeinsamen Funktion zu komponieren. Ein gutes Beispiel hierfür ist das Umrechnen eines Rechnungsbetrags in eine andere Währung anhand eines gegebenen Wechselkurses mit anschließendem Runden auf zwei Nachkommastellen. Die Funktion convert führt zunächst die Umrechnung durch:

var convert = function (amount, exchangeRate) {
return amount * exchangeRate;
};

Anschließend kann man das Ergebnis mit der Funktion round runden, wobei diese Funktion der Einfachheit halber auf die in JavaScript enthaltene Funktion Math.round zurückgreift:

var round = function (amount) {
return Math.round(amount * 100) / 100;
};

Um diese beiden Funktionen zu komponieren, muss man eine neue Funktion convertAndRound erzeugen, die zunächst convert aufruft und deren Ergebnis anschließend der Funktion round übergibt. Obwohl man diese Komposition auf einfache Art von Hand vornehmen kann, erleichtert der Einsatz einer generischen compose-Funktion dieses Vorhaben:

var compose = function (f, g) {
return function () {
return f(g.apply(null, arguments));
};
};

var convertAndRound = compose(round, convert);

Ruft man die von compose zurückgegebene Funktion nun mit 100 Euro und dem Wechselkurs von Euro zur DM auf, erhält man den umgerechneten und gerundeten Betrag:

var dm = convertAndRound(100, 1.95583);
console.log(dm); // => 195.58

Benötigt man ein anderes Rundungsverfahren, genügt es, die Funktion round auf andere Weise zu implementieren und die Komposition erneut auszuführen. Die convert-Funktion kann man für diese Anpassung vernachlässigen.

tl;dr: Der einfache Umgang mit Funktionen ist einer der wichtigsten Bausteine für die funktionale Programmierung. JavaScript unterstützt auf Basis des Konzepts von Funktionsausdrücken die Möglichkeit, Funktionen höherer Ordnung zu entwickeln. Diese benötigt man unter anderem für Rückruffunktionen, aber auch für die Funktionskomposition.