In den letzten Jahren haben sich anonyme Funktionen (AKA-Lambda-Funktionen) zu einem sehr beliebten Sprachkonstrukt entwickelt, und fast jede Haupt- / Mainstream-Programmiersprache hat sie eingeführt, oder es ist geplant, sie in einer bevorstehenden Überarbeitung des Standards einzuführen.
Anonyme Funktionen sind jedoch ein sehr altes und bekanntes Konzept in Mathematik und Informatik (erfunden von der Mathematikerin Alonzo Church um 1936 und verwendet von der Programmiersprache Lisp seit 1958, siehe z . B. hier ).
Warum haben die heutigen Standard-Programmiersprachen (von denen viele vor 15 bis 20 Jahren entstanden sind) Lambda-Funktionen von Anfang an nicht unterstützt und erst später eingeführt?
Und was hat in den letzten Jahren die massive Übernahme anonymer Funktionen ausgelöst? Gibt es ein bestimmtes Ereignis, eine neue Anforderung oder eine neue Programmiertechnik, die dieses Phänomen ausgelöst haben?
WICHTIGE NOTIZ
Der Schwerpunkt dieser Frage liegt auf der Einführung anonymer Funktionen in modernen Hauptsprachen (und daher mit wenigen Ausnahmen in nicht funktionalen Sprachen). Beachten Sie auch, dass anonyme Funktionen (Blöcke) in Smalltalk vorhanden sind, das keine funktionale Sprache ist, und dass normal benannte Funktionen auch in prozeduralen Sprachen wie C und Pascal seit langer Zeit vorhanden sind.
Bitte verallgemeinern Sie Ihre Antworten nicht, indem Sie von "der Übernahme des Funktionsparadigmas und seiner Vorteile" sprechen, da dies nicht das Thema der Frage ist.
quelle
Antworten:
Es ist sicherlich ein Trend zur funktionalen Programmierung oder zumindest zu bestimmten Aspekten festzustellen. Einige der populären Sprachen, die irgendwann anonyme Funktionen angenommen haben, sind C ++ ( C ++ 11 ), PHP ( PHP 5.3.0 ), C # ( C # v2.0 ), Delphi (seit 2009), Objective C ( Blöcke ) und Java 8 bringt Unterstützung für Lambdas in die Sprache . Und es gibt populäre Sprachen, die im Allgemeinen nicht als funktionstüchtig gelten, aber von Anfang an oder zumindest früh anonyme Funktionen unterstützen, zum Beispiel JavaScript.
Wie bei allen Trends ist der Versuch, nach einem einzelnen Ereignis zu suchen, das sie auslöste, wahrscheinlich eine Zeitverschwendung. In der Regel handelt es sich dabei um eine Kombination von Faktoren, von denen die meisten nicht quantifizierbar sind. Practical Common Lisp , veröffentlicht im Jahr 2005, hat möglicherweise eine wichtige Rolle dabei gespielt, Lisp als praktische Sprache neu in den Fokus zu rücken , da Lisp lange Zeit hauptsächlich eine Sprache war, die Sie in einem akademischen Umfeld oder in sehr spezifischen Nischenmärkten kennengelernt haben. Die Popularität von JavaScript hat möglicherweise auch eine wichtige Rolle dabei gespielt, anonyme Funktionen neu in den Fokus zu rücken, wie munificent in seiner Antwort erklärt .
Abgesehen von der Übernahme funktionaler Konzepte aus Mehrzwecksprachen ist auch eine Verlagerung hin zu funktionalen (oder überwiegend funktionalen) Sprachen festzustellen. Sprachen wie Erlang (1986), Haskell (1990), OCaml (1996), Scala (2003), F # (2005), Clojure (2007) und sogar domänenspezifische Sprachen wie R (1993) scheinen eine starke Anhängerschaft zu haben nachdem sie eingeführt wurden. Der allgemeine Trend hat die Aufmerksamkeit auf ältere funktionale Sprachen wie Scheme (1975) und offensichtlich Common Lisp gelenkt.
Ich denke, das einzig wichtigere Ereignis ist die Übernahme der funktionalen Programmierung in der Branche. Ich habe absolut keine Ahnung, warum dies früher nicht der Fall war, aber es scheint mir, dass die funktionale Programmierung irgendwann in den frühen und mittleren 90er Jahren ihren Platz in der Branche gefunden hat, angefangen (vielleicht) mit Erlangs Verbreitung in Telekommunikation und Haskells Übernahme in der Luft- und Raumfahrt sowie im Hardware-Design .
Joel Spolsky hat einen sehr interessanten Blog-Beitrag geschrieben, The Perils of JavaSchools , in dem er sich gegen den (damaligen) Trend der Universitäten ausspricht , Java anderen, vielleicht schwieriger zu erlernenden Sprachen vorzuziehen . Der Blogbeitrag hat zwar wenig mit funktionaler Programmierung zu tun, identifiziert jedoch ein zentrales Problem:
Ich erinnere mich noch, wie sehr ich Lisp hasste, als ich sie während meiner Studienzeit zum ersten Mal traf. Es ist definitiv eine harte Geliebte und es ist keine Sprache, in der man sofort produktiv sein kann (zumindest konnte ich das nicht). Im Vergleich zu Lisp ist Haskell (zum Beispiel) viel freundlicher, Sie können produktiv sein, ohne so viel Aufwand und ohne sich wie ein Vollidiot zu fühlen, und das könnte auch ein wichtiger Faktor bei der Umstellung auf funktionale Programmierung sein.
Alles in allem ist das eine gute Sache. Mehrere Mehrzwecksprachen verwenden Konzepte des Paradigmas, die den meisten ihrer Benutzer zuvor möglicherweise nicht zutreffend erschienen sind, und die Kluft zwischen den Hauptparadigmen verringert sich.
Verwandte Fragen:
Weitere Lektüre:
quelle
Ich finde es interessant, wie sehr die Popularität der funktionalen Programmierung mit dem Wachstum und der Verbreitung von Javascript einherging. Javascript hat viele radikale Merkmale entlang des funktionalen Programmierspektrums, die zum Zeitpunkt seiner Erstellung (1995) unter den gängigen Programmiersprachen (C ++ / Java) nicht sehr beliebt waren. Es wurde plötzlich in den Mainstream als einzige clientseitige Web-Programmiersprache eingeführt. Plötzlich mussten viele Programmierer einfach Javascript beherrschen, und deshalb musste man etwas über funktionierende Programmiersprachen-Funktionen wissen.
Ich frage mich, wie beliebt funktionierende Sprachen / Features wären, wenn nicht der plötzliche Aufstieg von Javascript gewesen wäre.
quelle
Ereignis JavaScript und DOM - Handler dazu geführt, dass Millionen von Programmierern hatten zumindest ein wenig über First - Class - Funktionen zu tun , um zu lernen , jede auf der Web - Interaktivität.
Von dort ist es ein relativ kurzer Schritt zu anonymen Funktionen. Da JavaScript nicht geschlossen wird
this
, empfiehlt es Ihnen auch dringend, mehr über Schließungen zu erfahren. Und dann bist du goldrichtig: Du verstehst anonyme erstklassige Funktionen, die über lexikalische Bereiche hinausgehen.Sobald Sie sich damit vertraut gemacht haben, möchten Sie es in jeder von Ihnen verwendeten Sprache.
quelle
Es ist sicherlich nicht der einzige Faktor, aber ich werde auf die Beliebtheit von Ruby hinweisen. Nicht zu sagen, dass dies wichtiger ist als eine der sechs Antworten, die bereits an der Tafel stehen, aber ich denke, dass viele Dinge gleichzeitig passiert sind und dass es nützlich ist, sie alle aufzuzählen.
Ruby ist keine funktionale Sprache, und ihre Lambdas, Prods und Blöcke scheinen klobig zu sein, wenn Sie so etwas wie ML verwenden. Fakt ist jedoch, dass sie den Begriff des Mappings und Reduzierens auf eine Generation junger Programmierer populär gemacht haben, die vor Java und PHP fliehen, um nach hipper zu suchen Weiden. Lambdas in mehreren Sprachen scheinen mehr als alles andere defensiv zu sein.
Aber die Block-Syntax und die Art und Weise, wie sie in .each, .map, .reduce usw. integriert ist, hat die Idee einer anonymen Funktion popluarisiert, auch wenn es sich tatsächlich um ein syntaktisches Konstrukt handelt, das sich wie eine Coroutine verhält. Und die einfache Konvertierung in einen Proc über & macht ihn zu einem Gateway-Medikament für die funktionale Programmierung.
Ich behaupte, dass Ruby on Rails-Programmierer, die JavaScript schreiben, bereits darauf eingestellt waren, Dinge in einem leichten funktionalen Stil zu tun. Zusammen mit dem Bloggen durch Programmierer verbreitete sich die Erfindung von Reddit, Hacker News und Stack Overflow etwa zur gleichen Zeit und die Ideen verbreiteten sich schneller über das Internet als zu Zeiten von Newsgroups.
TL; DR: Ruby, Rails, JavaScript, Blogging und Reddit / Hacker News / Stack Overflow haben funktionale Ideen auf den Massenmarkt gebracht, sodass jeder wollte, dass sie in vorhandenen Sprachen vorliegen, um weitere Fehler zu vermeiden.
quelle
Wie Yannis betonte, gibt es eine Reihe von Faktoren, die die Übernahme von Funktionen höherer Ordnung in Sprachen beeinflusst haben, die zuvor ohne waren. Einer der wichtigsten Punkte, auf die er nur kurz eingegangen ist, ist die Verbreitung von Multi-Core-Prozessoren und damit der Wunsch nach einer paralleleren und gleichzeitigeren Verarbeitung.
Der Map / Filter / Reduce-Stil der funktionalen Programmierung ist sehr parallelisierbar, sodass der Programmierer problemlos mehrere Kerne verwenden kann, ohne expliziten Threading-Code schreiben zu müssen.
Wie Giorgio bemerkt, geht es bei der funktionalen Programmierung nicht nur um Funktionen höherer Ordnung. Funktionen sowie ein Map / Filter / Reduce-Programmiermuster und Unveränderlichkeit bilden den Kern der funktionalen Programmierung. Zusammen ergeben diese Dinge leistungsstarke Werkzeuge für die parallele und gleichzeitige Programmierung. Zum Glück unterstützen viele Sprachen bereits den Begriff der Unveränderlichkeit, und selbst wenn dies nicht der Fall ist, können Programmierer die Dinge als unveränderlich behandeln, sodass Bibliotheken und Compiler asynchrone oder parallele Operationen erstellen und verwalten können.
Das Hinzufügen von Funktionen höherer Ordnung zu einer Sprache ist ein wichtiger Schritt, um die gleichzeitige Programmierung zu vereinfachen.
Aktualisieren
Ich werde ein paar detailliertere Beispiele hinzufügen, um auf die Bedenken von Loki einzugehen.
Betrachten Sie den folgenden C # -Code, der eine Sammlung von Widgets durchläuft und eine neue Liste von Widgetpreisen erstellt.
Für eine große Sammlung von Widgets oder eine rechenintensive CalculateWidgetPrice (Widget) -Methode würde diese Schleife verfügbare Kerne nicht sinnvoll nutzen. Um die Preisberechnungen für verschiedene Kerne durchführen zu können, muss der Programmierer Threads explizit erstellen und verwalten, die Arbeit weitergeben und die Ergebnisse zusammenfassen.
Betrachten Sie eine Lösung, sobald Funktionen höherer Ordnung zu C # hinzugefügt wurden:
Die foreach-Schleife wurde in die Select-Methode verschoben, wobei ihre Implementierungsdetails ausgeblendet wurden. Der Programmierer muss nur noch mitteilen, welche Funktion auf die einzelnen Elemente angewendet werden soll. Dies würde es der Select-Implementierung ermöglichen, die Berechnungen parallel auszuführen und alle Synchronisations- und Thread-Management-Probleme ohne Beteiligung des Programmierers zu lösen.
Aber Select funktioniert natürlich nicht parallel. Hier kommt Unveränderlichkeit ins Spiel. Die Implementierung von Select weiß nicht, dass die bereitgestellte Funktion (CalculateWidgets oben) keine Nebenwirkungen hat. Die Funktion kann den Status des Programms außerhalb der Ansicht von Select und seiner Synchronisierung ändern und alles beschädigen. In diesem Fall könnte beispielsweise der Wert von salesTax irrtümlich geändert werden. Reine funktionale Sprachen bieten Unveränderlichkeit, sodass die Select (Map) -Funktion sicher weiß, dass sich kein Zustand ändert.
C # behebt dies, indem es PLINQ als Alternative zu Linq bereitstellt. Das würde so aussehen:
Damit können Sie alle Kerne Ihres Systems voll ausnutzen, ohne diese Kerne explizit verwalten zu müssen.
quelle
cause
und ein,perceived affect
ohne das zu erklärencorrelation
. Die letzte Zeile IMO ist, worum es in der Frage geht; aber du hast nicht geantwortet. Warum vereinfacht es die gleichzeitige Programmierung?Ich stimme vielen der Antworten hier zu, aber was interessant ist, ist, dass ich, als ich etwas über Lambdas lernte und mich auf sie stürzte, aus keinem der Gründe, die andere angesprochen haben, darauf gestoßen bin.
In vielen Fällen verbessern Lambda-Funktionen einfach die Lesbarkeit Ihres Codes. Bevor Sie eine Methode aufrufen, die einen Funktionszeiger (oder eine Funktion oder einen Delegaten) akzeptiert, mussten Sie den Hauptteil dieser Funktion an einer anderen Stelle definieren. Wenn Sie also "foreach" -Konstrukte hatten, musste Ihr Leser zu einem anderen springen Teil des Codes, um zu sehen, was genau Sie mit den einzelnen Elementen vorhatten.
Wenn der Hauptteil der Funktion, die Elemente verarbeitet, nur aus wenigen Zeilen besteht, würde ich eine anonyme Funktion verwenden, da jetzt beim Lesen von Code die Funktionalität unverändert bleibt, der Leser jedoch nicht hin und her springen muss, sondern die gesamte Implementierung genau dort vor ihm.
Viele der funktionalen Programmiertechniken und Parallelisierungen könnten ohne anonyme Funktionen erreicht werden. deklarieren Sie einfach einen regulären und geben Sie einen Verweis darauf weiter, wann immer Sie müssen. Aber mit Lambdas wird das Schreiben von Code und das Lesen von Code erheblich vereinfacht.
quelle
Ich glaube, dass ein Faktor, der in die jüngste Geschichte einbezogen wurde, die Hinzufügung von Generika zu Java und .NET war. Das führt natürlich zu Func < , > und anderen stark typisierten Rechenabstraktionen (Task < >, Async < > etc.)
In der .NET-Welt haben wir diese Funktionen genau hinzugefügt, um FP zu unterstützen. Dies löste eine Reihe von kaskadierenden Sprachaufgaben im Zusammenhang mit der funktionalen Programmierung aus, insbesondere C # 3.0, LINQ, Rx und F #. Diese Entwicklung hat auch andere Ökosysteme beeinflusst und setzt sich bis heute in C #, F # und TypeScript fort.
Natürlich hilft es auch, wenn Haskell bei MSR arbeitet :)
Natürlich gab es auch viele andere Einflüsse (JS sicherlich) und diese Schritte wurden wiederum von vielen anderen Dingen beeinflusst - aber das Hinzufügen von Generika zu diesen Sprachen half, die starre OO-Orthodoxie der späten 90er Jahre in weiten Teilen der Software-Welt zu durchbrechen und zu öffnen die Tür für FP.
Don Syme
ps F # war 2003, nicht 2005 - obwohl wir sagen würden, dass es erst 2005 1.0 erreichte. Wir haben 2001-02 auch einen Haskell.NET-Prototyp erstellt.
quelle
Dies ist keine ernsthafte Antwort, aber die Frage erinnerte mich an einen witzigen Beitrag von James Iry - Eine kurze, unvollständige und meist falsche Geschichte der Programmiersprachen, der den folgenden Satz enthält:
"Lambdas werden in die relative Dunkelheit verbannt, bis Java sie populär macht, indem es sie nicht hat."
quelle
Nach allem, was ich sehe, konzentrieren sich die meisten Antworten darauf, zu erklären, warum die funktionale Programmierung im Allgemeinen ihr Comeback und ihren Weg in den Mainstream gefunden hat. Ich hatte das Gefühl, dass dies jedoch nicht wirklich die Frage nach anonymen Funktionen beantwortet und warum sie plötzlich so populär wurden.
Was wirklich an Popularität gewonnen hat, sind Verschlüsse . Da in den meisten Fällen Abschlüsse übergebene Wegwerffunktionen sind, ist es offensichtlich sinnvoll, für diese anonyme Funktionssyntax zu verwenden. In einigen Sprachen ist dies die einzige Möglichkeit, einen Abschluss zu erzielen.
Warum haben Verschlüsse an Popularität gewonnen? Weil sie bei der ereignisgesteuerten Programmierung beim Erstellen von Rückruffunktionen nützlich sind . Es ist derzeit die Art, JavaScript-Client-Code zu schreiben (tatsächlich ist es die Art, GUI-Code zu schreiben). Es ist derzeit auch die Art und Weise, hocheffizienten Back-End-Code sowie Systemcode zu schreiben, da in ereignisgesteuerten Paradigmen geschriebener Code normalerweise asynchron und nicht blockierend ist . Für das Back-End wurde dies als Lösung für das C10K-Problem populär .
quelle
Ich denke, der Grund dafür ist die zunehmende Verbreitung von gleichzeitiger und verteilter Programmierung, bei der der Kern der objektorientierten Programmierung (das Einkapseln des sich ändernden Zustands in Objekte) nicht mehr gilt. Im Falle eines verteilten Systems, denn es ist keine gemeinsamen Zustand (und Software - Abstraktionen dieses Konzepts sind undicht) und im Falle einer gleichzeitigen System, da richtig synchronisieren Zugriff auf gemeinsam genutzten Zustand ist umständlich und fehleranfällig erwiesen. Das heißt, einer der Hauptvorteile der objektorientierten Programmierung gilt nicht mehr für viele Programme, wodurch das objektorientierte Paradigma weitaus weniger hilfreich ist als früher.
Im Gegensatz dazu verwendet das Funktionsparadigma keinen veränderlichen Zustand. Alle Erfahrungen, die wir mit Funktionsparadigmen und -mustern gesammelt haben, sind daher sofort auf gleichzeitige und verteilte Berechnungen übertragbar. Und anstatt das Rad neu zu erfinden, leiht sich die Branche diese Muster und Sprachmerkmale, um ihren Bedürfnissen gerecht zu werden.
quelle
Wenn ich meine 0,02 € hinzufügen darf, obwohl ich der Wichtigkeit der Einführung des Konzepts durch JavaScript zustimmen würde, würde ich meiner Meinung nach mehr als die gleichzeitige Programmierung der aktuellen Mode der asynchronen Programmierung die Schuld geben. Bei asynchronen Aufrufen (die für die Webseiten erforderlich sind) sind einfache anonyme Funktionen offensichtlich so nützlich, dass sich jeder Webprogrammierer (dh jeder Programmierer) mit dem Konzept vertraut machen musste.
quelle
Ein anderes wirklich altes Beispiel für etwas, das anonymen Funktionen / Lambdas ähnelt, ist Call-by-Name in Algol 60. Beachten Sie jedoch, dass Call-by-Name der Übergabe von Makros als Parameter näher kommt als der Übergabe von True-Funktionen, und es ist fragiler / schwieriger als Ergebnis verstehen.
quelle
Hier die Abstammung nach bestem Wissen.
quelle
Anonyme Funktionen sind nett, weil es schwierig ist, Dinge zu benennen, und wenn Sie eine Funktion nur einmal verwenden, braucht sie keinen Namen.
Lambda-Funktionen haben sich erst in jüngster Zeit zum Mainstream entwickelt, da bis vor kurzem die meisten Sprachen Closures nicht unterstützten.
Ich würde vorschlagen, dass Javascript diesen Mainstream vorangetrieben hat. Es ist eine universelle Sprache, die keine Möglichkeit bietet, Parallelität auszudrücken, und anonyme Funktionen erleichtern die Verwendung von Rückrufmodellen. Zusätzlich haben populäre Sprachen wie Ruby und Haskell beigetragen.
quelle