Beispiel, das eine Schließung oder ein Lambda benötigt

8

Ich habe eine Stunde lang viele Beiträge auf dieser Seite und andere über lambdas und closures gelesen. Ich glaube ich verstehe was sie sind, d. H. wie sie arbeiten, aber ich verstehe nicht, warum sie existieren. Viele Beispiele, die ich sehe, beziehen sich vage auf ihre "Macht", aber in jedem dieser Fälle kann ich mir einen viel einfacheren Weg vorstellen, um zu erreichen, was dargestellt wird. Vielleicht liegt das daran, dass die Beispiele absichtlich zu einfach sind (um das Verständnis zu erleichtern), oder vielleicht bin ich dicht. Aber was ich wirklich gerne hätte, ist ein klares Beispiel von etwas zu sehen, was man mit einer Schließung oder einem Lambda erreichen kann, die man ohne eine solche nicht erreichen kann. Vielleicht ist das eine Frage, da alle Programmierparadigmen letztendlich auf die gleichen Maschinenbefehle übergehen und alles, was in einer Sprache gemacht werden kann, irgendwie auch in einer anderen Sprache erledigt werden kann. Ich denke also, dass ich wirklich nach einem Beispiel von etwas suche, das mit einem Verschluss gemacht wurde, der eleganter ist als ohne (was für ein Beispiel, das ich bisher gesehen habe, nicht der Fall zu sein scheint).

Hier ist, wovon ich rede.

Die führende Antwort für Was ist ein 'Closure'? , ein Beispiel in Scheme:

%Vor%

Ich kenne Schema wirklich nicht, aber ich denke, ich sehe was vor sich geht. Wäre dies jedoch nicht einfacher und dasselbe zu erreichen? Pseudocode:

%Vor%

Hier ist eine weitere auf Lambdas: Was ist eine Lambda (Funktion)?

Das angegebene JavaScript-Beispiel:

%Vor%

Noch einmal, warum geht das so? Warum nicht einfach sagen:

%Vor%

Jedes Beispiel, das ich gesehen habe, scheint eine übermäßig komplizierte Art zu sein, etwas zu tun, das leicht mit grundlegenden Funktionen erledigt werden kann. Diese Beispiele illustrieren sicherlich keine erstaunliche coole "Kraft", die ich sehen kann. Also bitte, jemand erleuchte mich, ich muss etwas verpassen. Danke.

    
The111 02.12.2012, 10:15
quelle

4 Antworten

5

Lambda-Kalkül ist eigentlich ziemlich einfach und nicht so nützlich für sich. Aber wenn Sie beginnen, die Implikationen des Lambda-Kalküls und der Strategien der Anwendungskontrolle mit Hilfe von funktionalen Entwurfsmustern zu erlernen, werden Sie ein besserer und leistungsfähigerer Programmierer werden.

Die Prämisse ist, dass Funktionen auch Werte sind und das macht es wirklich stark für das Abstrahieren vieler Konzepte. Ihre zwei Beispiele zeigen, wie Verschlüsse und Currying am einfachsten implementiert werden. Das sind triviale Beispiele. Wenn Sie beginnen, mit einem funktionalen Stil zu programmieren, werden diese Muster immer wieder als sehr mächtige Wege zur Abstraktion von Komplexität auftreten.

Trennung von Bedenken

Funktionale Programmierung ist nützlich, wenn Sie beginnen, mit Funktionen höherer Ordnung zu abstrahieren. Das typische Beispiel ist: Ich möchte etwas für eine Sammlung von Objekten tun.

Also in einem imperativen Programm, verwenden Sie eine for-Schleife:

%Vor%

Beachten Sie, dass die dosomething -Funktion im Codeblock genau in der Mitte der for-Schleife liegt. Sie können nicht wirklich trennen, was Sie mit jedem Element des Arrays tun, aus der tatsächlichen Struktur der Kontrollstruktur.

Bei Verwendung eines funktionalen Stils wird jedoch die Kontrollstruktur der Schleife mit map - einer Funktion höherer Ordnung - abstrahiert - Sie erhalten eine klare Trennung von zwei Konzepten der Schleifen- und Funktionsanwendung. Dies ist der äquivalente Code im Schema.

%Vor%

map kümmert sich darum, dass Sie jedes Element des Arrays durchlaufen. dosomething sorgt dafür, dass Sie für jedes Element des Arrays eine Operation ausführen. Das Nachdenken darüber wird viel klarer und die Änderung des Codes wird viel einfacher. Stellen Sie sich nun vor, dass jede for -Schleife in Ihrem Code durch dieses Konstrukt ersetzt wurde und wie viele Codezeilen es speichert.

billige Konstruktoren

Adressierung des Konzepts der Verschlüsse und Currying. Es gibt eine Äquivalenz zwischen Objekten und Funktionen. Im Wesentlichen können Funktionen so gestaltet werden, dass sie wie Objekte aussehen und sich verhalten. Javascript macht sich diese Tatsache zunutze. Alles, was Sie mit Objekten tun können, können Sie auch mit Funktionen tun. In der Tat, indem Sie die Unterscheidung zwischen dem, was ein Objekt ist und was eine Funktion ist, loslassen, haben Sie effektiv Ihre Gedanken gelöst und haben eine Art, über das Problem nachzudenken - das heißt, durch Code, der durch Funktionalität aufgebaut ist.

Ich benutze clojure code hier, weil ich damit zufrieden bin:

Dies ist der Clojure-Code für Trivialaddierer. Im Beispiel für Addierer rufen Sie ein addn mit der Nummer 5 auf, um eine Funktion zurückzubekommen, die 5 zu einer Zahl addiert. Ein allgemeiner Anwendungsfall könnte sein:

%Vor%

Angenommen, Sie hätten eine Funktion download-and-save-to , die eine URL und eine Datenbank verwendet, die URL in einer Datenbanktabelle speichert und bei Erfolg true zurückgibt, können Sie genau dieselbe Abstraktion wie mit + : %Vor%

Beachten Sie, dass die Struktur äquivalent ist, obwohl Sie sehr unterschiedliche Dinge tun. Denken Sie darüber nach, wie Sie dieses Problem mit Java oder C ++ angehen würden. Es würde viel mehr Code in Klassendefinitionen, Factory-Methoden, Klassen-Abstraktionen, Vererbung usw. enthalten. Sie müssen viel mehr Zeremonie durchlaufen, um den gleichen Effekt zu erzielen.

öffne deinen Geist

Wegen der Art und Weise, wie Sie Ihre Ideen so prägnant ausdrücken können, können Sie mit funktionaler Programmierung viele Konzepte erfassen, die in mehr ausführlichen Sprachen nur schwer auszudrücken sind. Das ausgewählte Werkzeug erleichtert die Suche nach bestimmten Problemen.

Ich empfehle clojure - (http://www.4clojure.com/) und lighttable - (http://www.lighttable.com/, Ссылка ) um loszulegen. Aus meiner persönlichen Erfahrung stammte mein einjähriges Lernen und Erforschen von Konzepten durch die clojure community ungefähr 10 Jahren des gemeinsamen Lernens von Java, C ++ und Python.

Oh, habe ich erwähnt, wie viel Spaß ich habe, all das Zeug zu lernen? Monaden, Kombinatoren, Vermehrer, Fortsetzungen ... all diese gruselig aussehenden akademischen Konzepte sind wirklich gut erreichbar, wenn Sie die richtigen Werkzeuge haben, um sie zu verstehen.

    
zcaudate 02.12.2012, 11:28
quelle
4

Diese Frage ist eher schwierig zu beantworten, weil Sie bereits alle Ihre Grundlagen abgedeckt haben: Sie haben bereits Beispiele, Beschreibungen, Erklärungen gesehen - und Sie werden sogar feststellen, dass sich Lambda-Ausdrücke in eine Maschinensprache zusammenfügen, die sie selbst nicht hat Lambda-Ausdrücke als Hauptmerkmal.

Trotzdem werde ich versuchen, ein Beispiel zu geben, das zu den häufigsten Anwendungsfällen zu gehören scheint, in denen ich persönlich einen Lambda-Ausdruck verwende. Ich benutze C #, was im Grunde eine Imperativ-Sprache ist, aber Lambda-Ausdrücke als eine eingebaute Funktion hat, so dass meine Beispiele in C # sind.

Stellen Sie sich vor, Sie haben eine Funktion, die einen Wert benötigt. Aber dieser Wert ist teuer zu berechnen, so sind die Anforderungen:

  • Wenn die Funktion den Wert nicht benötigt, wird sie überhaupt nicht ausgewertet.
  • Wenn die Funktion den Wert mehrmals benötigt, sollte sie nur einmal ausgewertet werden.

Lambda-Ausdrücke machen dies sehr einfach, da Sie erfasste Variablen verwenden können, um den berechneten Wert zu "cachen":

%Vor%

Jetzt kann ich getValue in irgendeine Funktion übergeben, und egal was diese Funktion damit macht (d. h. wie oft es aufgerufen wird), ich weiß, dass computeValue() nur einmal aufgerufen wird.

Um dasselbe ohne ein Lambda zu tun, müsste ich eine Klasse erstellen, die der Schließung dieses Lambda entspricht , und eine Art Schnittstelle implementieren, zum Beispiel:

%Vor%

Die Nachteile dieser großen Klasse sind:

  • Sie müssen für jede solche teure Berechnung eine neue Klasse schreiben. Das ist sehr repetitive Arbeit; Mit Lambda-Ausdrücken kann der Compiler das automatisieren.
  • Sie müssen für jede Art von Operation eine separate Schnittstelle schreiben.
Timwi 02.12.2012 10:39
quelle
2

Ich weiß nicht, ob Sie mit JavaScript vertraut sind, aber Closures und Lambdas sind ein grundlegendes Merkmal, um objektorientiertes Verhalten zu simulieren.

Betrachten Sie den folgenden Code:

%Vor%

In JavaScript gibt es nichts wie Klassen oder private Mitglieder , aber wir können Strukturen erstellen, die sich wie diese verhalten. Hier ist der Code mit der Erklärung dessen, was er macht:

%Vor%

In diesem Fall ist die an die Methode addEventListener übergebene Funktion eine Lambda-Funktion . Ja, Sie können es immer auf andere Weise definieren, aber es sind nur Details zur Implementierung. In diesem Fall könnte ich eine andere Variable in MyClass definition anstelle der Lambda-Funktion wie folgt erstellen:

%Vor%

Was kann ich Ihnen über JavaScript sagen? clickCounter befindet sich in dem Bereich closure der Lambda-Funktion, da er in einer Funktion oberhalb seiner Kette definiert ist ( MyClass ), aber clickHandler wäre auch in diesem Bereich vorhanden, nicht nur clickCounter .

Nehmen Sie an, dass diese Klasse einen Listener für 4 verschiedene Schaltflächen erstellen muss. Sie müssten für jeden einen Handler erstellen, und jedes Mal, wenn ein Trigger ausgelöst wird, sind alle Variablen in seinem Abschlussbereich vorhanden. Und 8 variable Referenzen (4 Handler + 4 Counter) zu haben ist schlimmer als nur die 4 Counter zu haben.

Ein weiterer Vorteil dieser Schließung ist, dass clickCounter ein privates Mitglied von MyClass ist, weil es als eine Variable in seinem Körper definiert ist und daher von außen nicht sichtbar ist. Das clickHandler (auch in seiner Lambda-Funktionsform) ist stattdessen innerhalb MyClass definiert, so dass es in der Lage ist, es durch die Schließung zu referenzieren.

Bearbeiten: Um clickCounter irgendwie nützlich zu machen, stellen Sie sich vor, dass Sie eine öffentliche Methode benötigen, die Ihnen sagt, ob der Zähler einen bestimmten Wert erreicht hat. Wir können es so schreiben (am Ende der Definition):

%Vor%

Sie könnten auch einen klassischen Getter und Setter haben, der einfach den Wert der privaten Variablen zurückgibt oder ändert, es hängt davon ab, was Sie brauchen. In dieser speziellen Implementierung möchte ich die private Variable nicht direkt verfügbar machen (und das ist sehr zu empfehlen, denn wenn die Klasse die einzige ist, die ihre Mitglieder zu manipulieren versteht, ist der Code leichter zu pflegen).

Hinweis: Beachten Sie, dass dies nur ein Beispiel für die Verwendung von Closures ist. Es gibt effizientere Möglichkeiten zum Definieren von Klassen in JavaScript!

    
4lbertoC 02.12.2012 11:30
quelle
0

Einige der anderen geposteten Antworten haben mithilfe von Schließungen referenziert, um private Variablen zu erstellen, aber ich denke, dieses Beispiel aus John Resigs fortgeschrittenem JavaScript-Tutorial macht es viel klarer. Vielleicht wird jemand anderes davon profitieren.

Quelle: Ссылка

%Vor%     
The111 29.03.2013 01:37
quelle