Was ist das für ein Lambda, von dem alle reden?


93

Was ist das für ein Lambda, von dem alle reden? Viele Leute scheinen es zu lieben, aber alles, was ich daraus ziehen kann, ist, dass es nur eine Möglichkeit ist, viele Codezeilen in einen einzigen Ausdruck zu packen.

Kann mich bitte jemand über seinen wahren Wert aufklären?


16
Kann ich die Antwortenden darauf hinweisen, dass der Fragesteller nirgends .net erwähnt


Knackende Frage. Danke für die Frage.
Erdnuss

Lambdas gehören zur Welt der funktionalen Programmierung (deklarative Programmierung).
RBT

Antworten:


179

Funktionen ohne Namen

Einfach ausgedrückt ist ein Lambda eine Funktion ohne Namen oder eine anonyme Funktion. Ein kleines Stück ausführbaren Codes, das wie eine Variable weitergegeben werden kann. In JavaScript:

function () {}; // very simple

Lassen Sie uns nun einige Verwendungszwecke für diese Lambdas sehen.

Kesselschildcode abstrahieren

Lambdas können verwendet werden, um Boilerplate-Code zu abstrahieren. Zum Beispiel Schleifen. Wir sind es gewohnt, den ganzen Tag zu schreiben forund zu whileschleifen. Dies ist jedoch Code, der nicht geschrieben wird. Wir könnten den Code innerhalb der Schleife, dem wichtigsten Teil der Schleife, extrahieren und den Rest abstrahieren:

for (var i=0; i<array.length; i++) {
    // do what something useful with array[i]
}

Durch die Verwendung forEachvon Array-Objekten wird:

array.forEach(function (element, index) {
   // do something useful with element
   // element is the equivalent of array[i] from above
});

Die obige Abstraktion mag nicht so nützlich sein, aber es gibt andere Funktionen höherer Ordnung forEach, die viel nützlichere Aufgaben ausführen. Zum Beispiel filter:

var numbers = [1, 2, 3, 4];
var even    = [];

// keep all even numbers from above array
for (var i=0; i<numbers.length; i++) {
    if (numbers[i] % 2 === 0) {
        even.push(numbers[i]);
    }
}

alert(even);

// Using the filter method
even = [1, 2, 3, 4].filter(function (number) {
    return number % 2 === 0;
});

alert(even);

Verzögerung bei der Codeausführung

In einigen Umgebungen, in denen das Konzept des Ereignisses verfügbar ist, können wir Lambdas verwenden, um auf Ereignisse zu reagieren, die zu einem bestimmten Zeitpunkt auftreten können.

window.onload = function () {
    alert("Loaded");
};

window.setTimeout(function () {
    alert("Code executed after 2 seconds.");
}, 2000);

Dies hätte auf andere Weise geschehen können, aber diese sind ziemlich ausführlich. In Java gibt es beispielsweise die RunnableSchnittstelle.

Funktionsfabriken

Bis zu diesem Zeitpunkt haben wir Lambdas hauptsächlich wegen seiner syntaktischen Zuckerfähigkeiten verwendet. Aber es gibt Situationen, in denen Lambdas viel nützlicher sein können. Zum Beispiel können wir Funktionen haben, die Lambdas zurückgeben. Angenommen, wir haben eine Funktion, deren Rückgabewerte zwischengespeichert werden sollen.

var users = [];
var getUser = function (name) {
    if (! users[name]) {
        // expensive operations to get a user. Ajax for example
        users[name] = user_from_ajax;
    }

    return users[name];
};

Später stellen wir möglicherweise fest, dass wir eine ähnliche Funktion haben:

var photos = [];
var getPhoto = function (name) {
    if (! photo[name]) {
        // expensive operations to get a user. Ajax for example
        photos[name] = photo_from_ajax;
    }

    return photos[name];
};

Da ist eindeutig ein Muster drin, also lasst es uns weg abstrahieren. Verwenden wir Memoization .

/**
 * @param {Array}     store Data structure in which we cache lambda's return values
 * @param {Function}  lambda
 * @return {Function} A function that caches the result of calling the lambda param
 */
var memoize = function (store, lambda) {
    // return a new lambda
    return function (name) {
        if (! store[name]) {
            // Execute the lambda and cache the result
            store[name] = lambda(name);
        }

        return store[name];
    };
};

var getUsers = memoize([], function (name) {
    // expensive operations to get a user. Ajax for example
});

var getPhotos = memoize([], function (name) {
    // expensive operations to get a photo. Ajax for example
});

Wie Sie sehen können, konnten wir mithilfe von Lambdas die Caching- / Memoisierungslogik abstrahieren. Wenn es für das andere Beispiel einige Problemumgehungen gab, glaube ich, dass dieses spezielle Problem mit anderen Techniken kaum gelöst werden kann. Wir haben es geschafft, einige wichtige Boilerplate-Codes an einem einzigen Ort zu extrahieren. Ganz zu schweigen davon, dass wir die usersund photosglobalen Variablen losgeworden sind.

Wenn ich mir dein Profil ansehe, sehe ich, dass du hauptsächlich Python-Benutzer bist. Für das obige Muster hat Python das Konzept der Dekorateure. Es gibt viele Beispiele im Internet für Memoization Decorators. Der einzige Unterschied besteht darin, dass Sie in Python höchstwahrscheinlich eine benannte verschachtelte Funktion in dieser Decorator-Funktion haben. Der Grund dafür ist, dass Python nur Lambdas mit einem Ausdruck unterstützt. Das Konzept ist jedoch dasselbe.

Als Beispiel für die Verwendung von Python Lambda. Der obige Code, in dem wir gerade Zahlen gefiltert haben, kann in Python folgendermaßen dargestellt werden:

filter(lambda x: x % 2 == 0, [1, 2, 3, 4])

Wie auch immer, Lambdas sind ohne Verschlüsse nicht so mächtig. Verschlüsse machen das Konzept der Lambdas so mächtig. In meinem Memoisierungsbeispiel habe ich Verschlüsse verwendet, um einen Verschluss um den storeParameter herum zu erstellen. Auf diese Weise habe ich auch dann Zugriff auf diesen Parameter, wenn die memoizeFunktion ihr Ergebnis zurückgegeben hat (ein Lambda).


3
Wow, du hast viel Zeit investiert.
mk12

4
@ Mk12, beim eigentlichen Schreiben der Antwort nicht wirklich. Ja, es ist einige Zeit her, seit ich angefangen habe :)
Ionuț G. Stan

Gute Antwort, aber fehlende Informationen zu "funktionalen Schnittstellen" (aus Java-Sicht).
Djangofan

Was sind diese "===" -Operatoren in Ihrem "Abstracting Boilerplate Code"?
Don


19

Der Begriff "Lambda" bezieht sich auf eine anonyme Funktion, normalerweise einen Verschluss . Sie sind nützlich, weil Sie damit Funktionen schreiben können, die andere Funktionen verwenden, ohne Ihren Code unnötig aufzublähen. Zum Beispiel in Ruby:

(1..100).select {|num| num % 2 == 0}

Dadurch wird ein Array erstellt, das gerade Zahlen zwischen 1 und 100 enthält. Wir müssen keine explizite Schleife schreiben. Die select-Methode verwendet eine Funktion, mit der die Werte getestet werden. Wir benötigen also nur unsere benutzerdefinierte Logik. Dies ermöglicht es uns, die Methode praktisch ohne Aufwand oder Aufwand stark anzupassen. Grundsätzlich können wir Funktionen aus kleineren Funktionen zusammensetzen.

Das ist nur ein einfaches Beispiel dafür, was sie tun können. Die Fähigkeit, Funktionen als Daten zu übergeben, ist sehr leistungsfähig und funktionale Sprachprogrammierer machen routinemäßig einige wirklich erstaunliche Dinge damit.


6
Vielleicht sollten Sie hinzufügen, dass das Ding in den Rohren der Parameter ist. Ich bin einer dieser Leute, die Probleme haben, Rubin zu lesen.
Skurmedel

Das ist eine schöne Antwort. Der einzige Grund, warum Ionut meine Stimme erhielt, ist, dass er uns sagte, warum wir uns (im Detail) um Lambdas kümmern sollten.
Frank Shearar

Ich würde nicht zustimmen, dass Lambdas normalerweise Verschlüsse sind.
JWG

6

"Lambda" vielleicht einfach zu wenig. Schauen Sie sich den Lambda-Kalkül an . Es ist nützlich bei der funktionalen Programmierung.

Und funktionale Programmierung ist ein weiteres Programmierparadigma (wie prozedural oder objektorientiert).


5
Dann sprechen die Leute über "Lambda", höchstwahrscheinlich über anonyme Funktionen, Funktionszeiger, Schließung oder ähnliches. Beziehen Sie sich fast nie auf die echte Lambda-Kalkül-Sache.
J-16 SDiZ

Schön, dass Sie den Lambda-Kalkül erwähnt haben! +1.
RBT

5

Lambdas in .NET werden häufig als "syntaktischer Zucker" bezeichnet. Sie wirken sich nicht direkt auf die Funktionalität aus, erleichtern jedoch die Verwendung der Sprache.

Wenn Sie die Möglichkeiten ihrer Verwendung verstanden haben, werden Sie sicher feststellen, dass Sie mit Delegaten / anonymen Methoden weniger Code schreiben als im alten Stil.


1
Ich glaube nicht, dass irgendjemand .NET erwähnt hat, daher ist das OP mit einer allgemeineren Antwort wahrscheinlich besser dran.
Wolf

2
Deshalb habe ich durch die Antwort auf .net klargestellt. Wenn andere mit ihrer Sprachimplementierung zirpen, helfen die Fragen und Antworten vielen Menschen, unabhängig von ihrer Sprachwahl.
Redsquare

Diese Antwort liest sich so, als hätten Sie etwas über Lambdas gehört, aber Sie selbst verstehen sie noch nicht.
JWG

2

Das Dr. Dobbs Journal enthält einen nützlichen Artikel, in dem Lambda-Ausdrücke vorgestellt werden (im Kontext von C ++, aber ich denke, Sie können die Prinzipien auf jede Sprache anwenden).

Wie der Artikel sagt: "Ein Lambda-Ausdruck ist ein sehr kompakter Ausdruck, für den keine separate Klassen- / Funktionsdefinition erforderlich ist."

Verwenden Sie also die Beispiele der Listings 1 und 2 von DDJ, anstatt zu schreiben:

std::for_each( vec.begin(), vec.end(), print_to_stream<std::string>(std::cout));

Was eine separate Klassendefinition erfordert wie:

template <typename T, typename Stream> class print_to_stream_t {
  Stream& stream_;
public:
  print_to_stream_t(Stream& s):stream_(s) {}
  void operator()(const T& t) const {
    stream_ << t;
  }
};
template <typename T,typename Stream> 
print_to_stream_t<T,Stream>   print_to_stream(Stream& s) {
  return print_to_stream_t<T,Stream>(s);
}

Mit der Boost-Lambda-Bibliothek kann dies werden:

std::for_each(vec.begin(),vec.end(),std::cout << _1);

Das hält die Definition inline.

Der Artikel erklärt auch einige weitere Anwendungen von Lambda-Ausdrücken.

Ich denke, ein wichtiger Punkt im DDJ-Artikel ist: "Normalerweise werden Lambda-Ausdrücke verwendet, wenn kleine und nicht übermäßig komplexe Funktionen an der Aufrufstelle benötigt werden. Wenn die Funktion nicht trivial wäre, würden Sie keinen Lambda-Ausdruck wünschen, sondern eine normale Funktion oder ein Funktionsobjekt. "


2

Wenn Sie jemals mit Funktionen / Methoden gearbeitet haben, die Funktionszeiger, Delegaten, Strategie- oder Beobachtermuster- / Ereignisbehandlung verwenden, und sich gedacht haben: "Ich schreibe diese ganze Funktion nur, um sie nur einmal zu verwenden - um sie an diese Methode zu übergeben ; Ich wünschte, ich könnte es einfach an Ort und Stelle schreiben, anstatt meinen Code zu überladen "- hier könnten Sie Lambda-Funktionen verwenden. Sprachen, die dieses Konstrukt unterstützen, nutzen in der Regel auch stark das Konzept der Übergabe von Funktionen als Parameter, insbesondere im Hinblick auf die Arbeit mit Listen (erstklassige Funktionen und Funktionen höherer Ordnung). Dies gilt insbesondere für funktionale Sprachen, die sich bei Berechnungen eher auf die Funktionszusammensetzung als auf die Speichermodifikation stützen. In einigen Fällen (in Sprachen wie Python),


2

"Lambda" als Wort ist eine Terminologie aus der Zeit, als Informatik-Typen genauso wahrscheinlich in Mathematik oder Logik ausgebildet wurden wie ein Informatik-Abschluss. Einige von ihnen haben sich ein Paradigma ausgedacht, das sich "funktionale Programmierung" nennt, ganz anders als imperativ und auch ziemlich mächtig. AFAIK , das ist das Milieu, in dem der Begriff verwendet wurde.

Mathematiker und Logiker verwenden seltsame Wörter.

'Lambda' klingt wirklich esoterisch - als ob sie eine sehr seltsame und besondere Sache wären. Wenn Sie JavaScript für eine Webbrowser-Anwendung schreiben und die Redewendung "var foo = function () {...}" verwenden, haben Sie die ganze Zeit über Lambda-Funktionen verwendet.


1

Ein Lambda-Ausdruck ist eine einfache Form der Funktion. Die Idee ist, dass etwas von der Form auf der linken Seite (äquivalent zu Parametern) etwas von der Form auf der rechten Seite (äquivalent zum Körper) wird.

Zum Beispiel in cis:

x => x * x

ist ein Lambda, um einen Wert zu quadrieren. Etwas von der Form

x

wird etwas von der Form

x * x

0

"Ein Lambda-Ausdruck ist eine anonyme Funktion, die Ausdrücke und Anweisungen enthalten kann und zum Erstellen von Delegaten oder Ausdrucksbaumtypen verwendet werden kann.

Alle Lambda-Ausdrücke verwenden den Lambda-Operator =>, der als "geht nach" gelesen wird. Die linke Seite des Lambda-Operators gibt die Eingabeparameter an (falls vorhanden) und die rechte Seite enthält den Ausdrucks- oder Anweisungsblock. Der Lambda-Ausdruck x => x * x lautet "x geht zu x mal x".

von MSDN


4
Ja, Microsoft lässt alle glauben, sie hätten es erfunden. Lambda-Ausdrücke sind jedoch älter als Microsoft. Es ist ein mathematischer Begriff, der auf mehrere Programmiersprachen angewendet wurde. (Was möglich ist, da Mathematik für sich genommen eine Computersprache sein könnte.)
Wim ten Brink

4
Beachten Sie, dass dies spezifisch für die .NET-Implementierung von Microsoft ist. Es ist keine große Abweichung von der allgemeinen Idee eines Lambda, aber ich denke, die in Lisp implementierte Funktionalität ist eher "Standard".
Chuck

2
Diese Antwort erklärt überhaupt nicht, was ein Lambda ist (benötigt Definitionen von anonymer Funktion, Delegat, Ausdrucksbaumtyp) und erklärt sicherlich nicht, was sein Wert ist.
Danio

0

Eine vollständige Erklärung zu Lambda-Ausdrücken finden Sie auch in Wikipedia . (Scrollen Sie zum Teil Lambda-Kalkül und Programmiersprachen .) Lambda-Ausdrücke sind nicht so neu und nicht nur Teil von C #, sondern etwas, das vor fast 80 Jahren in das Rechnen eingeführt wurde! Lambda-Ausdrücke sind die Basis für die funktionale Programmierung.

Dessen Wert? Nun, wenn man bedenkt, dass es eigentlich ziemlich alt ist, würde ich sagen: sehr wertvoll für jeden, der Berechnungen durchführt.


0

Wenn Sie sich für Java interessieren, haben Sie in den letzten Monaten viel über Lambdas oder Schließungen gehört, da es verschiedene Vorschläge gab, diese Funktion zu Java 7 hinzuzufügen. Ich denke jedoch, dass das Komitee sie fallen gelassen hat. Einer der Vorschläge stammt von Neal Gafter und wird hier ausführlich erläutert: javac.info . Dies half mir, die Anwendungsfälle und Vorteile zu verstehen (insbesondere gegenüber inneren Klassen).




-1

Ja, es ist nur eine Möglichkeit, viele Codezeilen in einen einzigen Ausdruck zu packen. Da es sich jedoch um ein so effizientes Cramming handelt, können Sie Ihr Programm auf neue Weise strukturieren.

Oft würde man das Schreiben von Delegaten oder Rückrufen vermeiden und zum prozeduralen Stil zurückkehren, einfach weil es zu viel Arbeit ist, neue Funktionen oder Klassen für einen einzelnen Ausdruck zu deklarieren.

Mit Lambda-Ausdrücken lohnt es sich, Rückrufe auch für kleinste Aufgaben zu verwenden, wodurch der Code möglicherweise klarer wird. Möglicherweise nicht.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.