Die Lambda-Berechnung ist ein Berechnungsmodell, das in den 30er Jahren von Alonzo Church erfunden wurde. Die Syntax und die Semantik der meisten funktionalen Programmiersprachen sind direkt oder indirekt von der Lambda-Rechnung inspiriert.
Der Lambda-Kalkül hat in seiner grundlegendsten Form zwei Operationen: Abstraktion (Erstellen einer (anonymen) Funktion) und Anwendung (Anwenden einer Funktion). Die Abstraktion erfolgt mit dem λ-Operator, der dem Lambda-Kalkül seinen Namen gibt.
- Lambda-Ausdrücke
- Lambda funktioniert
Anonyme Funktionen werden oft als "Lambdas", "Lambda-Funktionen" oder "Lambda-Ausdrücke" bezeichnet, da λ, wie oben erwähnt, das Symbol für die Erstellung anonymer Funktionen im Lambda-Kalkül war (und das Wort lambda
für die Erstellung anonymer Funktionen in vielen lisp verwendet wird -basierte Sprachen aus dem gleichen Grund).
Dies ist kein allgemein gebräuchlicher Begriff, aber ich gehe davon aus, dass dies das Programmieren mit anonymen Funktionen oder das Programmieren mit Funktionen höherer Ordnung bedeutet.
Ein bisschen mehr Informationen über Lambdas in C ++ 0x, ihre Motivation und ihre Beziehung zu Funktionszeigern (eine Menge davon ist wahrscheinlich eine Wiederholung dessen, was Sie bereits wissen, aber ich hoffe, es hilft, die Motivation von Lambdas zu erklären und wie sie sich unterscheiden von Funktionszeigern):
Funktionszeiger, die es in C bereits gab, sind sehr nützlich, um z. B. eine Vergleichsfunktion an eine Sortierfunktion zu übergeben. Ihre Nützlichkeit ist jedoch begrenzt:
Wenn Sie beispielsweise einen Vektor von Vektoren nach dem i
th-Element jedes Vektors sortieren möchten (wobei i
es sich um einen Laufzeitparameter handelt), können Sie dies nicht mit einem Funktionszeiger lösen. Eine Funktion, die zwei Vektoren nach ihrem i
th-Element vergleicht, müsste drei Argumente ( i
und die beiden Vektoren) annehmen , während die Sortierfunktion eine Funktion benötigt, die zwei Argumente annimmt. Was wir brauchen, ist eine Möglichkeit, das Argument irgendwie i
an die Funktion zu übergeben, bevor es an die Sortierfunktion übergeben wird, aber wir können dies nicht mit einfachen C-Funktionen tun.
Um dies zu lösen, führte C ++ das Konzept der "Funktionsobjekte" oder "Funktoren" ein. Ein Funktor ist im Grunde ein Objekt, das eine operator()
Methode hat. Jetzt können wir eine Klasse definieren CompareByIthElement
, die das Argument i
als Konstruktorargument und dann die beiden zu vergleichenden Vektoren als Argumente für die operator()
Methode verwendet. Um einen Vektor von Vektoren nach dem i
th-Element zu sortieren, können wir jetzt ein CompareByIthElement
Objekt mit i
als Argument erstellen und dieses Objekt dann an die Sortierfunktion übergeben.
Da Funktionsobjekte nur Objekte und keine technischen Funktionen sind (obwohl sie sich wie diese verhalten sollen), können Sie einen Funktionszeiger nicht auf ein Funktionsobjekt verweisen lassen (Sie können natürlich einen Zeiger auf ein Funktionsobjekt haben, aber es hätte einen Typ wie CompareByIthElement*
und wäre somit kein Funktionszeiger).
Die meisten Funktionen in der C ++ - Standardbibliothek, die Funktionen als Argumente verwenden, werden mithilfe von Vorlagen definiert, sodass sie sowohl mit Funktionszeigern als auch mit Funktionsobjekten arbeiten.
Nun zu Lambdas:
Das Definieren einer ganzen Klasse zum Vergleichen durch das i
th-Element ist etwas ausführlich, wenn Sie sie nur einmal zum Sortieren eines Vektors verwenden. Selbst wenn Sie nur einen Funktionszeiger benötigen, ist die Definition einer benannten Funktion suboptimal, wenn sie nur einmal verwendet wird, da a) sie den Namespace verschmutzt und b) die Funktion normalerweise sehr klein ist und es nicht wirklich eine gibt Ein guter Grund, die Logik in eine eigene Funktion zu abstrahieren (ansonsten können Sie keine Funktionszeiger haben, ohne eine Funktion zu definieren).
Um dies zu beheben, wurden Lambdas eingeführt. Lambdas sind Funktionsobjekte, keine Funktionszeiger. Wenn Sie ein Lambda-Literal verwenden, wird ein ähnlicher [x1, x2](y1,y2){bla}
Code generiert, der im Grunde Folgendes bewirkt:
- Definieren Sie eine Klasse mit zwei Mitgliedsvariablen (
x1
und x2
) und einem operator()
mit den Argumenten ( y1
und y2
) und dem Hauptteil bla
.
- Erstellen Sie eine Instanz der Klasse, die Einstellung der Elementvariablen
x1
und x2
auf die Werte der Variablen x1
und x2
zur Zeit in ihrem Umfang.
Lambdas verhalten sich also wie Funktionsobjekte, mit der Ausnahme, dass Sie nicht auf die Klasse zugreifen können, die generiert wurde, um ein Lambda auf eine andere Weise als mit dem Lambda zu implementieren. Folglich akzeptiert jede Funktion, die Funktoren als Argumente akzeptiert (was im Grunde genommen jede Nicht-C-Funktion in der Standardbibliothek bedeutet), Lambdas, aber jede Funktion, die nur Funktionszeiger akzeptiert, nicht.