function practice - Warum sind Python-Lambdas nützlich?




complex bind (22)

Ich versuche herauszufinden, Python Lambdas. Ist Lambda einer jener "interessanten" Sprachartikel, die im wirklichen Leben vergessen werden sollten?

Ich bin mir sicher, dass es einige Randfälle gibt, in denen es nötig sein könnte, aber angesichts der Unklarheit dessen, das Potenzial, dass es in zukünftigen Versionen neu definiert wird (meine Annahme basiert auf den verschiedenen Definitionen davon) und die reduzierte Codierungsklarheit - sollte es Gemieden werden?

Das erinnert mich an einen Überlauf (Pufferüberlauf) von C-Typen, der auf die oberste Variable zeigt und die anderen Feldwerte überlädt. Es fühlt sich an wie ein Techie-Showmanship, aber ein Coder-Albtraum.


Answers

Wie oben erwähnt, definiert der Lambda-Operator in Python eine anonyme Funktion, und in Python-Funktionen sind Closures. Es ist wichtig, das Konzept der Verschlüsse nicht mit dem Operator Lambda zu verwechseln, bei dem es sich lediglich um syntaktisches Methadon handelt.

Als ich vor ein paar Jahren in Python angefangen habe, habe ich sehr viel lambdas benutzt und dachte, dass sie cool sind, zusammen mit List Comprehensions. Ich schrieb und muss jedoch eine große Website in Python in der Größenordnung von mehreren tausend Funktionspunkten pflegen. Ich habe aus Erfahrung gelernt, dass Lambdas in Ordnung sein können, um Dinge zu prototypieren, aber nichts über Inline-Funktionen (sogenannte Closures) bieten, außer dass man ein paar Key-Stokes speichert oder manchmal nicht.

Grundsätzlich läuft das auf mehrere Punkte hinaus:

  • Es ist einfacher, Software zu lesen, die explizit mit aussagekräftigen Namen geschrieben wurde. Anonyme Verschlüsse können definitionsgemäß keinen aussagekräftigen Namen haben, da sie keinen Namen haben. Diese Kürze scheint aus irgendeinem Grund auch Lambda-Parameter zu infizieren, daher sehen wir oft Beispiele wie Lambda x: x + 1
  • Es ist einfacher, Named-Closures wiederzuverwenden, da sie mehr als einmal mit Namen bezeichnet werden können, wenn es einen Namen gibt, auf den sie verweisen.
  • Es ist einfacher, Code zu debuggen, der named closures anstelle von lambdas verwendet, da der Name in tracebacks und um den Fehler herum angezeigt wird.

Das ist Grund genug, sie zusammenzufassen und in Namensschließungen umzuwandeln. Ich halte jedoch zwei weitere Groll gegen anonyme Schließungen.

Der erste Groll ist einfach, dass sie nur ein weiteres unnötiges Schlüsselwort sind, das die Sprache verstopft.

Der zweite Groll ist tiefer und auf Paradigma-Ebene, dh ich mag es nicht, dass sie einen funktionalen Programmierstil fördern, weil dieser Stil weniger flexibel ist als der Nachrichtenübergabe-, objektorientierte oder prozedurale Stil, weil der Lambda-Kalkül nicht Turing- ist. vollständig (zum Glück können wir in Python diese Einschränkung sogar innerhalb eines Lambda ausbrechen). Die Gründe, warum ich lambdas für diesen Stil halte, sind:

  • Es gibt eine implizite Rückkehr, dh sie scheinen so zu sein, als ob sie Funktionen wären.

  • Sie sind ein alternativer Mechanismus zum Verbergen von Zuständen zu einem anderen, expliziteren, besser lesbaren, wiederverwendbaren und allgemeineren Mechanismus: Methoden.

Ich bemühe mich, lambda-freies Python zu schreiben und lambdas auf Sicht zu entfernen. Ich denke Python wäre eine etwas bessere Sprache ohne Lambdas, aber das ist nur meine Meinung.


Ich bezweifle, dass Lambda weggehen wird. Sieh Guido's Post darüber, endlich aufzugeben, es zu entfernen. Siehe auch einen Überblick über den Konflikt .

Sie können diesen Beitrag für mehr eine Geschichte über den Deal hinter Pythons funktionalen Funktionen http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html : http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html

Kurioserweise wurden die Funktionen zum Zuordnen, Filtern und Reduzieren, die ursprünglich die Einführung von Lambda und anderen funktionalen Merkmalen motiviert hatten, zu einem großen Teil durch Listenerläuterungen und Generatorausdrücke ersetzt. Tatsächlich wurde die Reduce-Funktion aus der Liste der eingebauten Funktionen in Python 3.0 entfernt. (Es ist jedoch nicht notwendig, Beschwerden über die Entfernung von Lambda, Karte oder Filter zu senden: sie bleiben. :-)

Meine eigenen zwei Cent: Selten ist Lambda es wert, soweit es die Klarheit geht. Im Allgemeinen gibt es eine klarere Lösung, die Lambda nicht enthält.


Lambda is a procedure constructor. You can synthesize programs at run-time, although Python's lambda is not very powerful. Note that few people understand that kind of programming.


Ich kann nicht mit Pythons spezieller Implementierung von Lambda sprechen, aber im Allgemeinen sind Lambda-Funktionen wirklich praktisch. Sie sind eine Kerntechnik (vielleicht sogar DIE Technik) der funktionalen Programmierung, und sie sind auch sehr nützlich in objektorientierten Programmen. Für bestimmte Arten von Problemen sind sie die beste Lösung, also sollte nicht vergessen werden!

Ich schlage vor, dass Sie die Closures und die map-Funktion (die auf Python-Dokumente verweist, aber in fast jeder Sprache existiert, die funktionale Konstrukte unterstützt) nachlesen können, um zu sehen, warum sie nützlich ist.


Erste Gratulation, dass es gelungen ist, Lambda herauszufinden. Meiner Meinung nach ist dies ein wirklich mächtiges Konstrukt, um damit zu handeln. Der Trend hin zu funktionalen Programmiersprachen ist sicherlich ein Indikator dafür, dass er weder vermieden werden sollte noch in naher Zukunft neu definiert wird.

Du musst nur ein bisschen anders denken. Ich bin mir sicher, bald wirst du es lieben. Aber sei vorsichtig, wenn du nur mit Python arbeitest. Weil das Lambda kein echter Verschluss ist, ist es irgendwie "gebrochen": Pythons Lambda ist gebrochen


Ich benutze Python seit ein paar Jahren und ich bin nie in einen Fall gelaufen, in dem ich Lambda benötigt habe. Wirklich, wie das tutorial sagt, ist es nur für syntaktischen Zucker.


I use lambdas to avoid code duplication. It would make the function easily comprehensible Eg:

def a_func()
  ...
  if some_conditon:
     ...
     call_some_big_func(arg1, arg2, arg3, arg4...)
  else
     ...
     call_some_big_func(arg1, arg2, arg3, arg4...)

I replace that with a temp lambda

def a_func()
  ...
  call_big_f = lambda args_that_change: call_some_big_func(arg1, arg2, arg3, args_that_change)
  if some_conditon:
     ...
     call_big_f(argX)
  else
     ...
     call_big_f(argY)

A useful case for using lambdas is to improve the readability of long list comprehensions . In this example loop_dic is short for clarity but imagine loop_dic being very long. If you would just use a plain value that includes i instead of the lambda version of that value you would get a NameError .

>>> lis = [{"name": "Peter"}, {"name": "Josef"}]

>>> loop_dic = lambda i: {"name": i["name"] + " Wallace" }
>>> new_lis = [loop_dic(i) for i in lis]

>>> new_lis
[{'name': 'Peter Wallace'}, {'name': 'Josef Wallace'}]

Anstatt von

>>> lis = [{"name": "Peter"}, {"name": "Josef"}]

>>> new_lis = [{"name": i["name"] + " Wallace"} for i in lis]

>>> new_lis
[{'name': 'Peter Wallace'}, {'name': 'Josef Wallace'}]

I use lambda to create callbacks that include parameters. It's cleaner writing a lambda in one line than to write a method to perform the same functionality.

Beispielsweise:

import imported.module

def func():
    return lambda: imported.module.method("foo", "bar")

as opposed to:

import imported.module

def func():
    def cb():
        return imported.module.method("foo", "bar")
    return cb

I use it quite often, mainly as a null object or to partially bind parameters to a function.

Here are examples:

to implement null object pattern:

{
    DATA_PACKET: self.handle_data_packets
    NET_PACKET: self.handle_hardware_packets
}.get(packet_type, lambda x : None)(payload)

for parameter binding:

let say that I have the following API

def dump_hex(file, var)
    # some code
    pass

class X(object):
    #...
    def packet_received(data):
        # some kind of preprocessing
        self.callback(data)
    #...

Then, when I wan't to quickly dump the recieved data to a file I do that:

dump_file = file('hex_dump.txt','w')
X.callback = lambda (x): dump_hex(dump_file, x)
...
dump_file.close()

Lambda-Funktion ist es eine unbürokratische Möglichkeit, eine Funktion zu erstellen.

Das ist es. Nehmen wir zum Beispiel an, Sie haben Ihre Hauptfunktion und müssen Werte quadrieren. Lass uns den traditionellen Weg und den Lambda-Weg sehen, dies zu tun:

Traditioneller Weg:

def main():
...
...
y = square(some_number)
...
return something

def square(x):
    return x**2

Der Lambda-Weg:

def main():
...
square = lambda x: x**2
y = square(some_number)
return something

Sieh den Unterschied?

Lambda-Funktionen passen sehr gut zu Listen, wie Listen Comprehensions oder Map. In der Tat, Listenverständnis ist es eine "pythonische" Möglichkeit, sich mit Lambda auszudrücken. Ex:

>>>a = [1,2,3,4]
>>>[x**2 for x in a]
[1,4,9,16]

Mal sehen, was jedes Element der Syntax bedeutet:

[]: "Gib mir eine Liste"

x ** 2: "Verwenden dieser neuen Funktion"

für x in a: "in jedes Element in einem"

Das ist bequem, äh? Funktionen wie diese erstellen. Lass es uns mit Lambda umschreiben:

>>> square = lambda x: x**2
>>> [square(s) for x in a]
[1,4,9,16]

Jetzt benutzen wir map, was dasselbe ist, aber sprachneutraler. Maps benötigt 2 Argumente:

(i) eine Funktion

(ii) ein iterierbares

Und gibt Ihnen eine Liste, in der jedes Element die Funktion ist, die auf jedes Element des Iterablen angewendet wird.

Also, mit der Karte hätten wir:

>>> a = [1,2,3,4]
>>> squared_list = map(lambda x: x**2, a)

Wenn Sie lambdas und Mapping beherrschen, haben Sie eine große Macht, Daten zu manipulieren. Lambda-Funktionen sind weder obskur noch entziehen sie der Code-Klarheit. Verwechsle nicht etwas Hartes mit etwas Neuem. Sobald Sie anfangen, sie zu benutzen, werden Sie es sehr klar finden.


I can give you an example where I actually needed lambda serious. I'm making a graphical program, where the use right clicks on a file and assigns it one of three options. It turns out that in Tkinter (the GUI interfacing program I'm writing this in), when someone presses a button, it can't be assigned to a command that takes in arguments. So if I chose one of the options and wanted the result of my choice to be:

print 'hi there'

Then no big deal. But what if I need my choice to have a particular detail. For example, if I choose choice A, it calls a function that takes in some argument that is dependent on the choice A, B or C, TKinter could not support this. Lamda was the only option to get around this actually...


Die zweizeilige Zusammenfassung:

  1. Closures : Sehr nützlich. Lerne sie, benutze sie, liebe sie.
  2. Pythons lambda Schlüsselwort: unnötig, gelegentlich nützlich. Wenn Sie feststellen, dass Sie irgendetwas entfernt komplexes damit tun, legen Sie es weg und definieren Sie eine echte Funktion.

Ich finde Lambda nützlich für eine Liste von Funktionen, die das gleiche tun, aber für verschiedene Umstände. Wie die Mozilla Pluralregeln .

plural_rules = [
    lambda n: 'all',
    lambda n: 'singular' if n == 1 else 'plural',
    lambda n: 'singular' if 0 <= n <= 1 else 'plural',
    ...
]
# Call plural rule #1 with argument 4 to find out which sentence form to use.
plural_rule[1](4) # returns 'plural'

Wenn Sie für alle eine Funktion definieren müssten, würden Sie am Ende verrückt werden. Es wäre auch nicht nett mit Funktionsnamen wie plural_rule_1 , plural_rule_2 , etc. Und Sie müssten es eval() , wenn Sie auf eine Variable Funktion ID plural_rule_2 sind.


Lambdas sind tatsächlich sehr mächtige Konstrukte, die aus Ideen in der funktionalen Programmierung stammen, und es ist etwas, das in naher Zukunft von Python auf keinen Fall einfach überarbeitet, neu definiert oder entfernt werden wird. Sie helfen Ihnen, Code zu schreiben, der leistungsfähiger ist, da Sie Funktionen als Parameter weitergeben können, also die Idee von Funktionen als erstklassige Bürger.

Lambdas tendieren dazu, verwirrend zu werden, aber sobald ein solides Verständnis erreicht ist, kannst du sauberen, eleganten Code wie diesen schreiben:

squared = map(lambda x: x*x, [1, 2, 3, 4, 5])

Die obige Codezeile gibt eine Liste der Quadrate der Zahlen in der Liste zurück. Natürlich könntest du es auch so machen:

def square(x):
    return x*x

squared = map(square, [1, 2, 3, 4, 5])

Es ist offensichtlich, dass der frühere Code kürzer ist, und dies gilt insbesondere, wenn Sie die Map-Funktion (oder eine ähnliche Funktion, die eine Funktion als Parameter akzeptiert) nur an einer Stelle verwenden möchten. Dies macht den Code auch intuitiver und eleganter.

Auch, wie David Zaslavsky in seiner Antwort erwähnt hat, sind Listenkompromittierungen nicht immer der richtige Weg, besonders wenn Ihre Liste Werte von einem obskuren mathematischen Weg erhalten muss.

Aus praktischer Sicht war einer der größten Vorteile von Lambdas für mich in letzter Zeit die GUI- und ereignisgesteuerte Programmierung. Wenn Sie Rückrufe in Tkinter betrachten, nehmen sie als Argumente nur das Ereignis an, das sie ausgelöst hat. Z.B

def define_bindings(widget):
    widget.bind("<Button-1>", do-something-cool)

def do-something-cool(event):
    #Your code to execute on the event trigger

Was wäre, wenn Sie einige Argumente hätten, die bestanden werden? Etwas so einfaches wie das Übergeben von 2 Argumenten zum Speichern der Koordinaten eines Mausklicks. Du kannst es leicht so machen:

def main():
    # define widgets and other imp stuff
    x, y = None, None
    widget.bind("<Button-1>", lambda event: do-something-cool(x, y))

def do-something-cool(event, x, y):
    x = event.x
    y = event.y
    #Do other cool stuff

Jetzt können Sie argumentieren, dass dies mit globalen Variablen getan werden kann, aber wollen Sie sich wirklich den Kopf schütteln, wenn Sie sich Gedanken über die Speicherverwaltung und das Leaking machen, besonders wenn die globale Variable nur an einem bestimmten Ort verwendet wird? Das wäre nur ein schlechter Programmierstil.

Kurz gesagt, Lambdas sind fantastisch und sollten nie unterschätzt werden. Python-Lambdas sind zwar nicht die gleichen wie LISP-Lambdas (die mächtiger sind), aber man kann wirklich eine Menge magischer Dinge mit ihnen machen.


Ich habe heute David Mertz 'Buch' Text Processing in Python 'gelesen. Während er eine ziemlich knappe Beschreibung von Lambda hat, haben die Beispiele im ersten Kapitel, kombiniert mit der Erklärung in Anhang A, dazu geführt, dass sie für mich (auf der Stelle) von der Seite gesprungen sind, und ich verstand plötzlich ihren Wert. That is not to say his explanation will work for you and I am still at the discovery stage so I will not attempt to add to these responses other than the following: I am new to Python I am new to OOP Lambdas were a struggle for me Now that I read Mertz, I think I get them and I see them as very useful as I think they allow a cleaner approach to programming.

He reproduces the Zen of Python, one line of which is Simple is better than complex. As a non-OOP programmer reading code with lambdas (and until last week list comprehensions) I have thought- This is simple? . I finally realized today that actually these features make the code much more readable, and understandable than the alternative-which is invariably a loop of some sort. I also realized that like financial statements-Python was not designed for the novice user, rather it is designed for the user that wants to get educated. I can't believe how powerful this language is. When it dawned on me (finally) the purpose and value of lambdas I wanted to rip up about 30 programs and start over putting in lambdas where appropriate.


lambda ist nur eine originelle Art, function sagen. Abgesehen von seinem Namen gibt es nichts, das obskur, einschüchternd oder kryptisch ist. Wenn Sie die folgende Zeile lesen, ersetzen Sie lambda durch function in Ihrem Kopf:

>>> f = lambda x: x + 1
>>> f(3)
4

Es definiert nur eine Funktion von x . Einige andere Sprachen, wie R , sagen es ausdrücklich:

> f = function(x) { x + 1 }
> f(3)
4

Siehst du? Es ist eines der natürlichsten Dinge in der Programmierung.


Ich fange gerade mit Python an und rannte kopfüber nach Lambda - was mich eine Weile brauchte, um es herauszufinden.

Beachten Sie, dass dies keine Verurteilung von irgendetwas ist. Jeder hat andere Dinge, die nicht einfach sind.

Ist Lambda einer dieser "interessanten" Sprachartikel, die im wirklichen Leben vergessen werden sollten?

Nein.

Ich bin mir sicher, dass es einige Grenzfälle gibt, wo es nötig sein könnte, aber angesichts der Dunkelheit,

Es ist nicht obskur. Die letzten 2 Teams, an denen ich gearbeitet habe, nutzten diese Funktion die ganze Zeit.

das Potential, dass es in zukünftigen Releases neu definiert wird (meine Annahme basiert auf den verschiedenen Definitionen davon)

Ich habe keine ernsthaften Vorschläge gesehen, um es in Python neu zu definieren, nachdem ich vor ein paar Jahren noch die Semantik des Abschlusses festgelegt hatte.

und die reduzierte Codierungsklarheit - sollte es vermieden werden?

Es ist nicht weniger klar, wenn Sie es richtig verwenden. Im Gegenteil, mehr verfügbare Sprachkonstrukte erhöhen die Klarheit.

Das erinnert mich an Überlauf (Pufferüberlauf) von C-Typen - zeigt auf die oberste Variable und überladen, um die anderen Feldwerte zu setzen ... Art von Technik-Showman aber Wartung Coder Albtraum ..

Lambda ist wie Pufferüberlauf? Beeindruckend. Ich kann mir nicht vorstellen, wie Sie Lambda verwenden, wenn Sie denken, dass es ein "Wartungs-Albtraum" ist.


Eine der netten Sachen über lambda , die meiner Meinung nach untertrieben ist, ist, dass es eine Möglichkeit ist, eine Bewertung für einfache Formulare zu verschieben, bis der Wert benötigt wird. Lassen Sie mich erklären.

Viele Bibliotheksroutinen sind so implementiert, dass sie bestimmte Parameter als Callables zulassen (von denen Lambda eins ist). Die Idee ist, dass der tatsächliche Wert nur zu dem Zeitpunkt berechnet wird, zu dem er verwendet wird (eher als wenn er aufgerufen wird). Ein (erfundenes) Beispiel könnte helfen, den Punkt zu veranschaulichen. Angenommen, Sie haben eine Routine, die einen bestimmten Zeitstempel protokolliert. Sie möchten, dass die Routine die aktuelle Zeit minus 30 Minuten verwendet. Du würdest es so nennen

log_timestamp(datetime.datetime.now() - datetime.timedelta(minutes = 30))

Angenommen, die tatsächliche Funktion wird nur aufgerufen, wenn ein bestimmtes Ereignis eintritt und der Zeitstempel nur zu diesem Zeitpunkt berechnet werden soll. Sie können das so machen

log_timestamp(lambda : datetime.datetime.now() - datetime.timedelta(minutes = 30))

Angenommen, der log_timestamp kann log_timestamp wie diesen behandeln, wird er dies bei Bedarf auswerten und Sie erhalten den Zeitstempel zu diesem Zeitpunkt.

Es gibt natürlich alternative Möglichkeiten, dies zu tun (zum Beispiel mit dem operator Modul), aber ich hoffe, ich habe den Punkt vermittelt.

Update : Here ist ein etwas konkreteres Beispiel aus der realen Welt.

Update 2 : Ich denke, dies ist ein Beispiel dafür, was ein thunk .


Lambdas sind im Allgemeinen tief mit dem funktionalen Programmierstil verbunden. Die Idee, dass Sie Probleme lösen können, indem Sie eine Funktion auf einige Daten anwenden und die Ergebnisse zusammenführen, verwendet Google, um die meisten seiner Algorithmen zu implementieren.

Programme, die in einem funktionalen Programmierstil geschrieben sind, können leicht parallelisiert werden und werden daher mit modernen Mehrkern-Maschinen immer wichtiger. Kurz gesagt, NEIN sollten Sie sie nicht vergessen.


Ein Lambda ist Teil eines sehr wichtigen Abstraktionsmechanismus, der sich mit Funktionen höherer Ordnung beschäftigt. Um das richtige Verständnis für seinen Wert zu bekommen, schauen Sie sich die hochwertigen Lektionen von Abelson und Sussman an und lesen Sie das Buch SICP

Dies sind relevante Themen im modernen Softwaregeschäft und werden immer beliebter.


Es gibt eine Menge Verwirrung um Lambdas und Schließungen, sogar in den Antworten auf diese -Frage hier. Anstatt zufällige Programmierer zu fragen, die von Programmiersperren mit bestimmten Programmiersprachen oder anderen ahnungslosen Programmierern erfahren haben, machen Sie eine Reise zur Quelle (wo alles angefangen hat). Und da Lambdas und Verschlüsse von Lambda Calculus stammen , die von Alonzo Church in den 30er Jahren erfunden wurden, bevor es überhaupt elektronische Computer gab, ist dies die Quelle, von der ich spreche.

Lambda-Kalkül ist die einfachste Programmiersprache der Welt. Die einzigen Dinge, die Sie darin tun können:

  • ANWENDUNG: Anwenden eines Ausdrucks auf einen anderen, bezeichnet als fx .
    (Stellen Sie sich einen Funktionsaufruf vor , wobei f die Funktion und x der einzige Parameter ist)
  • ABSTRACTION: Bindet ein in einem Ausdruck vorkommendes Symbol, um zu markieren, dass dieses Symbol nur ein "Slot" ist, ein leeres Feld, das darauf wartet, mit einem Wert gefüllt zu werden, eine "Variable" sozusagen. Dazu wird ein griechischer Buchstabe λ (Lambda) vorangestellt, dann der symbolische Name (zB x ), dann ein Punkt . vor dem Ausdruck. Dies konvertiert dann den Ausdruck in eine Funktion, die einen Parameter erwartet.
    Zum Beispiel: λx.x+2 nimmt den Ausdruck x+2 und teilt mit, dass das Symbol x in diesem Ausdruck eine gebundene Variable ist - es kann durch einen Wert ersetzt werden, den Sie als Parameter angeben.
    Beachten Sie, dass die auf diese Weise definierte Funktion anonym ist - sie hat keinen Namen, Sie können sich also noch nicht darauf beziehen, aber Sie können sie sofort aufrufen (Anwendung merken), indem Sie ihr den Parameter, auf den sie wartet, z dies: (λx.x+2) 7 . Dann wird der Ausdruck (in diesem Fall ein Literalwert) 7 als x in den Teilausdruck x+2 des angewendeten Lambda substituiert, so dass Sie 7+2 , was dann durch allgemeine Arithmetikregeln auf 9 reduziert wird.

Also haben wir eines der Geheimnisse gelöst:
Lambda ist die anonyme Funktion aus dem obigen Beispiel, λx.x+2 .

In verschiedenen Programmiersprachen kann die Syntax für die funktionale Abstraktion (Lambda) unterschiedlich sein. Zum Beispiel sieht es in JavaScript so aus:

function(x) { return x+2; }

und Sie können es sofort auf einen der folgenden Parameter anwenden:

function(x) { return x+2; } (7)

oder Sie können diese anonyme Funktion (Lambda) in eine Variable speichern:

var f = function(x) { return x+2; }

Das gibt ihm effektiv einen Namen f , so dass Sie darauf verweisen und es später mehrfach aufrufen können, zB:

alert(  f(7) + f(10)  );   // should print 21 in the message box

Aber du musstest es nicht nennen. Du könntest es sofort anrufen:

alert(  function(x) { return x+2; } (7)  );  // should print 9 in the message box

In LISP werden Lambdas wie folgt gemacht:

(lambda (x) (+ x 2))

und Sie können solch ein Lambda aufrufen, indem Sie es sofort auf einen Parameter anwenden:

(  (lambda (x) (+ x 2))  7  )

OK, jetzt ist es Zeit, das andere Rätsel zu lösen: Was ist eine Schließung? Um das zu tun, lassen Sie uns über Symbole ( Variablen ) in Lambda-Ausdrücken sprechen.

Wie gesagt, was die Lambda-Abstraktion tut, bindet ein Symbol in seinen Teilausdruck, so dass es zu einem substituierbaren Parameter wird . Ein solches Symbol wird als gebunden bezeichnet . Aber was, wenn es andere Symbole im Ausdruck gibt? Zum Beispiel: λx.x/y+2 . In diesem Ausdruck ist das Symbol x durch die Lambda-Abstraktion λx. gebunden λx. vor ihm. Aber das andere Symbol, y , ist nicht gebunden - es ist frei . Wir wissen nicht, was es ist und woher es kommt, also wissen wir nicht, was es bedeutet und welchen Wert es repräsentiert, und deshalb können wir diesen Ausdruck nicht bewerten, bis wir herausgefunden haben, was y bedeutet.

Das Gleiche gilt für die beiden anderen Symbole 2 und + . Es ist nur so, dass wir diese beiden Symbole so gut kennen, dass wir normalerweise vergessen, dass der Computer sie nicht kennt und wir ihnen sagen müssen, was sie bedeuten, indem wir sie irgendwo definieren, zB in einer Bibliothek oder in der Sprache selbst.

Sie können sich die freien Symbole so vorstellen, wie sie irgendwo außerhalb des Ausdrucks in ihrem "umgebenden Kontext" definiert sind, der ihre Umgebung genannt wird . Die Umgebung könnte ein größerer Ausdruck dafür sein, dass dieser Ausdruck ein Teil von (wie Qui-Gon Jinn sagte: "Es gibt immer einen größeren Fisch";)) oder in einer Bibliothek oder in der Sprache selbst (als ein Primitiv ) ist.

Dadurch können wir Lambda-Ausdrücke in zwei Kategorien einteilen:

  • GESCHLOSSENE Ausdrücke: Jedes Symbol, das in diesen Ausdrücken auftritt, ist durch eine Lambda-Abstraktion gebunden . Mit anderen Worten, sie sind in sich geschlossen ; Sie benötigen keinen umgebenden Kontext, um ausgewertet zu werden. Sie werden auch Kombinatoren genannt .
  • OPEN-Ausdrücke: Einige Symbole in diesen Ausdrücken sind nicht gebunden - das heißt, einige der in ihnen vorkommenden Symbole sind frei und sie benötigen einige externe Informationen und können daher nicht ausgewertet werden, bis Sie die Definitionen dieser Symbole angeben.

Sie können einen offenen Lambda-Ausdruck SCHLIESSEN, indem Sie die Umgebung bereitstellen, die all diese freien Symbole definiert, indem sie sie an einige Werte bindet (die Zahlen, Strings, anonyme Funktionen, Lambda, usw. sein können).

Und hier kommt der Verschlussteil :
Das Schließen eines Lambda-Ausdrucks ist eine bestimmte Menge von Symbolen, die im äußeren Kontext (Umgebung) definiert sind und den freien Symbolen in diesem Ausdruck Werte geben, so dass sie nicht mehr frei sind. Es verwandelt einen offenen Lambda-Ausdruck, der noch einige "undefinierte" freie Symbole enthält, in einen geschlossenen , der keine freien Symbole mehr hat.

Wenn Sie beispielsweise den folgenden Lambda-Ausdruck haben: λx.x/y+2 , ist das Symbol x gebunden, während das Symbol y frei ist. Daher ist der Ausdruck open und kann nicht ausgewertet werden, wenn Sie nicht sagen, was y bedeutet (und der gleich mit + und 2 , die auch frei sind). Angenommen, Sie haben auch eine Umgebung wie diese:

{  y: 3,  +: [built-in addition],  2: [built-in number],  q: 42,  w: 5  }

Diese Umgebung liefert Definitionen für alle "undefinierten" (freien) Symbole aus unserem Lambda-Ausdruck ( y , + , 2 ) und einigen zusätzlichen Symbolen ( q , w ). Die Symbole, die wir definieren müssen, sind diese Teilmenge der Umgebung:

{  y: 3,  +: [built-in addition],  2: [built-in number]  }

und das ist genau die Schließung unseres Lambda-Ausdrucks:>

Mit anderen Worten, es schließt einen offenen Lambda-Ausdruck. Dies ist, wo die Namensschließung von vornherein kam, und deshalb sind so viele Antworten der Leute in diesem Faden nicht ganz richtig: P

Also warum irren sie sich? Warum sagen so viele von ihnen, dass es sich bei Closures um einige Datenstrukturen im Speicher oder um einige Merkmale der Sprachen handelt, die sie verwenden, oder warum sie Closures mit Lambda verwechseln? : P

Nun, die Unternehmens-Markttreiber von Sun / Oracle, Microsoft, Google usw. sind schuld, denn so haben sie diese Konstrukte in ihren Sprachen (Java, C #, Go etc.) genannt. Sie nennen oft "closures", was nur Lambdas sein soll. Oder sie nennen "closures" eine bestimmte Technik, mit der sie das lexikalische Scoping implementiert haben, dh die Tatsache, dass eine Funktion auf die Variablen zugreifen kann, die zum Zeitpunkt ihrer Definition in ihrem äußeren Umfang definiert wurden. Sie sagen oft, dass die Funktion diese Variablen "umschließt", das heißt, sie in eine Datenstruktur einfängt, um zu verhindern, dass sie zerstört werden, nachdem die äußere Funktion ihre Ausführung beendet hat. Aber das ist nur erfunden Post-Faktum "Folklore-Etymologie" und Marketing, die nur die Dinge verwirrender macht, weil jeder Sprachanbieter seine eigene Terminologie verwendet.

Und es ist noch schlimmer wegen der Tatsache, dass es immer ein bisschen Wahrheit in dem gibt, was sie sagen, was es nicht erlaubt, es leicht als falsch zu verwerfen: P Lass mich erklären:

Wenn Sie eine Sprache implementieren möchten, die Lambdas als erstklassige Bürger verwendet, müssen Sie ihnen erlauben, Symbole zu verwenden, die in ihrem umgebenden Kontext definiert sind (dh, Sie verwenden freie Variablen in Ihren Lambda-Tags). Und diese Symbole müssen auch dann vorhanden sein, wenn die umgebende Funktion zurückkehrt. Das Problem besteht darin, dass diese Symbole an einen lokalen Speicher der Funktion gebunden sind (normalerweise auf dem Aufruf-Stack), der bei der Rückkehr der Funktion nicht mehr vorhanden ist. Damit ein Lambda wie erwartet funktioniert, müssen Sie also alle diese freien Variablen irgendwie aus ihrem äußeren Kontext "erfassen" und für später speichern, auch wenn der äußere Kontext verschwunden ist. Das heißt, Sie müssen die Schließung Ihres Lambdas (alle diese externen Variablen, die es verwendet) finden und es woanders speichern (entweder indem Sie eine Kopie erstellen oder indem Sie Platz für sie im Voraus an anderer Stelle als auf dem Stapel vorbereiten). Die tatsächliche Methode, die Sie verwenden, um dieses Ziel zu erreichen, ist ein "Implementierungsdetail" Ihrer Sprache. Wichtig ist hier die Schließung , die die Menge der freien Variablen aus der Umgebung Ihres Lambda darstellt, die irgendwo gespeichert werden müssen.

Es dauerte nicht lange, bis die Leute begannen, die tatsächliche Datenstruktur aufzurufen, die sie in den Implementierungen ihrer Sprache verwenden, um die Schließung als "Schließung" selbst zu implementieren. Die Struktur sieht normalerweise so aus:

Closure {
   [pointer to the lambda function's machine code],
   [pointer to the lambda function's environment]
}

und diese Datenstrukturen werden als Parameter an andere Funktionen weitergegeben, von Funktionen zurückgegeben und in Variablen gespeichert, um Lambdas darzustellen und ihnen den Zugriff auf ihre umschließende Umgebung sowie den Maschinencode zu ermöglichen, der in diesem Kontext ausgeführt wird. Aber es ist nur eine Möglichkeit (eine von vielen), die Schließung zu implementieren , nicht die Schließung selbst.

Wie ich oben erklärt habe, ist das Schließen eines Lambda-Ausdrucks die Teilmenge von Definitionen in seiner Umgebung, die den freien Variablen in diesem Lambda-Ausdruck Werte geben, die den Ausdruck effektiv schließen (einen offenen Lambda-Ausdruck verwandeln, der noch nicht ausgewertet werden kann) ein geschlossener Lambda-Ausdruck, der dann ausgewertet werden kann, da alle darin enthaltenen Symbole definiert sind.

Alles andere ist nur ein "Cargo-Kult" und "voo-doo magic" von Programmierern und Sprachanbietern, die sich der wahren Wurzeln dieser Begriffe nicht bewusst sind.

Ich hoffe das beantwortet deine Fragen. Aber wenn Sie weitere Fragen haben, können Sie sie in den Kommentaren fragen, und ich werde versuchen, es besser zu erklären.





python function lambda closures