Hier gibt es einige gute Antworten. Eine interessante Sache, die nicht vollständig geklärt ist, ist, dass deklarativ und imperativ komplementär und symbiotisch sind, mehr als nur verschiedene Stile oder was gegen wie .
Kit
1
@ Kit Imo, einige der Antworten auf dieser Seite bringen die Begriffe zusammen. DP == referentielle Transparenz (RT). DP & IP sind Gegensätze, daher sind Afaics keine Komplemente eines Ganzen, dh ein ganzes Programm kann in beiden Stilen geschrieben werden. Der Aufruf einer Funktion kann entweder DP (RT) oder IP sein, die Implementierung kann entweder oder eine Mischung sein. Sie sind nicht symbiotisch in dem Sinne, dass ein Aufruf einer IP-Funktion in einer ansonsten DP-Funktion die Aufruf-IP der DP-Funktion bewirken kann. Sie sind in dem Sinne symbiotisch, dass reale (z. B. funktionale reaktive) Programme eine Mischung verwenden können, z. B. IP-Top-Level-Aufrufe in DP-Funktionen.
Zum Zeitpunkt des Schreibens sind die Antworten mit den höchsten Stimmen auf dieser Seite ungenau und durcheinander in der deklarativen vs. imperativen Definition, einschließlich der Antwort, die Wikipedia zitiert. Einige Antworten verbinden die Begriffe auf unterschiedliche Weise.
Lesen Sie auch meine Erklärung, warum die Tabellenkalkulationsprogrammierung deklarativ ist, unabhängig davon, ob die Formeln die Zellen mutieren.
In mehreren Antworten wird auch behauptet, dass die funktionale Programmierung eine Teilmenge der deklarativen sein muss. Von diesem Punkt hängt es ab, ob wir "Funktion" von "Prozedur" unterscheiden. Behandeln wir zuerst Imperativ vs. Deklarativ.
Definition des deklarativen Ausdrucks
Das einzige Attribut, das einen deklarativen Ausdruck möglicherweise von einem imperativen Ausdruck unterscheiden kann, ist die referenzielle Transparenz (RT) seiner Unterausdrücke. Alle anderen Attribute werden entweder von beiden Ausdruckstypen gemeinsam genutzt oder von der RT abgeleitet.
Eine zu 100% deklarative Sprache (dh eine, in der jeder mögliche Ausdruck RT ist) erlaubt (neben anderen RT-Anforderungen) nicht die Mutation gespeicherter Werte, z. B. HTML und den größten Teil von Haskell.
Definition der RT-Expression
RT wird oft als "ohne Nebenwirkungen" bezeichnet. Der Begriff Effekte hat keine genaue Definition, daher stimmen einige Leute nicht darin überein, dass "keine Nebenwirkungen" mit RT identisch sind. RT hat eine genaue Definition .
Da jeder Unterausdruck konzeptionell ein Funktionsaufruf ist, erfordert RT, dass die Implementierung einer Funktion (dh der Ausdruck (der Ausdrücke) innerhalb der aufgerufenen Funktion) möglicherweise nicht auf den veränderlichen Zustand zugreift, der außerhalb der Funktion liegt (der Zugriff auf den veränderlichen lokalen Zustand ist) dürfen). Einfach ausgedrückt sollte die Funktion (Implementierung) rein sein .
Definition der reinen Funktion
Eine reine Funktion soll oft "keine Nebenwirkungen" haben. Der Begriff Effekte hat keine genaue Definition, daher stimmen einige Leute nicht zu.
Reine Funktionen haben die folgenden Attribute.
Die einzige beobachtbare Ausgabe ist der Rückgabewert.
Die einzige Ausgabeabhängigkeit sind die Argumente.
Argumente werden vollständig bestimmt, bevor eine Ausgabe generiert wird.
Denken Sie daran, dass RT für Ausdrücke (einschließlich Funktionsaufrufe) und Reinheit für (Implementierungen von) Funktionen gilt.
Ein obskures Beispiel für unreine Funktionen, die RT-Ausdrücke erzeugen, ist die Parallelität. Dies liegt jedoch daran, dass die Reinheit auf der Interrupt-Abstraktionsschicht unterbrochen ist. Das musst du nicht wirklich wissen. Um RT-Ausdrücke zu erstellen, rufen Sie reine Funktionen auf.
Abgeleitete Attribute von RT
Jedes andere für die deklarative Programmierung angeführte Attribut, z. B. das von Wikipedia verwendete Zitat aus dem Jahr 1999 , stammt entweder von RT oder wird mit der imperativen Programmierung geteilt. Dies beweist, dass meine genaue Definition korrekt ist.
Beachten Sie, dass die Unveränderlichkeit externer Werte eine Teilmenge der Anforderungen für RT ist.
Deklarative Sprachen haben keine Kontrollstrukturen Looping, zB forund while, weil aufgrund Unveränderlichkeit , die Schleifenbedingung würde nie ändern.
Deklarative Sprachen drücken keinen anderen Kontrollfluss als die verschachtelte Funktionsreihenfolge (auch als logische Abhängigkeiten bezeichnet) aus, da andere Optionen der Bewertungsreihenfolge aufgrund der Unveränderlichkeit das Ergebnis nicht ändern (siehe unten).
Deklarative Sprachen drücken logische "Schritte" aus (dh die verschachtelte Reihenfolge der RT-Funktionsaufrufe), aber ob jeder Funktionsaufruf eine Semantik höherer Ebene ist (dh "was zu tun ist"), ist keine Voraussetzung für die deklarative Programmierung. Der Unterschied zum Imperativ besteht darin, dass diese "Schritte" aufgrund der Unveränderlichkeit (dh allgemeiner RT) nicht vom veränderlichen Zustand abhängen können, sondern nur von der relationalen Reihenfolge der ausgedrückten Logik (dh der Reihenfolge der Verschachtelung der Funktionsaufrufe, auch Unterausdrücke genannt) ).
Beispielsweise kann der HTML-Absatz <p>erst angezeigt werden, wenn die Unterausdrücke (dh Tags) im Absatz ausgewertet wurden. Es gibt keinen veränderlichen Zustand, nur eine Ordnungsabhängigkeit aufgrund der logischen Beziehung der Tag-Hierarchie (Verschachtelung von Unterausdrücken, die analog verschachtelte Funktionsaufrufe sind ).
Somit gibt es das abgeleitete Attribut der Unveränderlichkeit (allgemeiner RT), dass deklarative Ausdrücke nur die logischen Beziehungen der Bestandteile (dh der Argumente der Unterausdrucksfunktion) und nicht veränderbare Zustandsbeziehungen ausdrücken .
Auswertungsreihenfolge
Die Wahl der Auswertungsreihenfolge von Unterausdrücken kann nur dann zu einem variierenden Ergebnis führen, wenn einer der Funktionsaufrufe nicht RT ist (dh die Funktion ist nicht rein), z. B. wird innerhalb der Funktion auf einen veränderlichen Zustand außerhalb einer Funktion zugegriffen.
Zum Beispiel einiger verschachtelten Ausdrücke, zum Beispiel gegeben f( g(a, b), h(c, d) ), eifrig und faul Auswertung der Argumente Funktion der gleichen Ergebnisse liefern , wenn die Funktionen f, gund hrein sind.
Wenn dagegen die Funktionen f, gund hkann nicht rein ist , dann ist die Wahl der Bewertungs um ein anderes Ergebnis geben.
Beachten Sie, dass verschachtelte Ausdrücke konzeptionell verschachtelte Funktionen sind, da Ausdrucksoperatoren nur Funktionsaufrufe sind, die sich als unäres Präfix, unäres Postfix oder binäre Infixnotation tarnen.
Tangential, wenn alle Bezeichner, zum Beispiel a, b, c, dsind, unveränderlich überall, Zustand außerhalb des Programms nicht zugegriffen werden kann (dh I / O), und es gibt keine Abstraktionsschicht Bruch, dann sind Funktionen immer rein.
Haskell hat übrigens eine andere Syntax f (g a b) (h c d).
Details zur Auswertungsreihenfolge
Eine Funktion ist ein Zustandsübergang (kein veränderlicher gespeicherter Wert) vom Eingang zum Ausgang. Für RT-Kompositionen von Aufrufen von reinen Funktionen ist die Ausführungsreihenfolge dieser Zustandsübergänge unabhängig. Der Zustandsübergang jedes Funktionsaufrufs ist aufgrund fehlender Nebenwirkungen und des Prinzips, dass eine RT-Funktion durch ihren zwischengespeicherten Wert ersetzt werden kann, unabhängig von den anderen . Um ein weit verbreitetes Missverständnis zu korrigieren , ist die reine monadische Komposition immer deklarativ und RT , trotz der Tatsache, dass Haskells IOMonade wohl unrein und daher für den WorldZustand außerhalb des Programms unabdingbar ist (aber im Sinne der Einschränkung unten die Nebenwirkungen) isoliert sind).
Eifrige Auswertung bedeutet, dass die Funktionsargumente ausgewertet werden, bevor die Funktion aufgerufen wird, und verzögerte Auswertung bedeutet, dass die Argumente erst ausgewertet werden, wenn (und wenn) innerhalb der Funktion auf sie zugegriffen wird.
Definition : Funktionsparameter werden in der Funktion deklariert Definition Standort und Funktionsargumente werden an der Funktion geliefert Anruf Ort. Kennen Sie den Unterschied zwischen Parameter und Argument .
Konzeptionell alle Ausdrücke sind (eine Zusammensetzung) Funktionsaufrufe, z Konstanten sind Funktionen ohne Eingänge, unäre Operatoren sind Funktionen mit einem Eingang, binary Infixoperatoren sind Funktionen mit zwei Eingängen, Konstruktoren sind Funktionen, und auch Steueranweisungen (z if, for, while) kann mit Funktionen modelliert werden. Die Reihenfolge , dass diese Argumentation Funktionen (mit verschachteltem Funktionsaufruf , um nicht zu verwechseln tun) ausgewertet wird nicht durch die Syntax erklärt, zum Beispiel f( g() )könnte eifrig bewertet gdann fauf g‚s Ergebnis oder es könnte bewerten fund nur träge bewerten , gwenn das Ergebnis innerhalb benötigt f.
Vorbehalt, keine vollständige Turing- Sprache (dh die eine unbegrenzte Rekursion ermöglicht) ist vollkommen deklarativ, z. B. führt eine träge Auswertung Gedächtnis- und Zeitindeterminismus ein. Diese Nebenwirkungen aufgrund der Wahl der Auswertungsreihenfolge sind jedoch auf den Speicherverbrauch, die Ausführungszeit, die Latenz, die Nichtbeendigung und die externe Hysterese und damit auf die externe Synchronisation beschränkt.
Funktionsprogrammierung
Da deklarative Programmierung keine Schleifen haben kann, ist die einzige Möglichkeit zum Iterieren die funktionale Rekursion. In diesem Sinne bezieht sich die funktionale Programmierung auf die deklarative Programmierung.
Die funktionale Programmierung macht die Funktion normalerweise zu einem erstklassigen Objekt, was bedeutet, dass der Funktionstyp in der Grammatik überall dort erscheinen kann, wo es einen anderen Typ gibt. Das Ergebnis ist, dass Funktionen Funktionen eingeben und bearbeiten können, wodurch eine Trennung von Bedenken durch Hervorheben der Funktionszusammensetzung erreicht wird, dh die Abhängigkeiten zwischen den Teilberechnungen einer deterministischen Berechnung getrennt werden.
Anstatt beispielsweise eine separate Funktion zu schreiben (und anstelle von Schleifen eine Rekursion zu verwenden, wenn die Funktion auch deklarativ sein muss), für jede einer unendlichen Anzahl möglicher spezialisierter Aktionen, die auf jedes Element einer Sammlung angewendet werden könnten, verwendet die funktionale Programmierung eine wiederverwendbare Iteration Funktionen, zB map, fold, filter. Diese Iterationsfunktionen geben eine erstklassige spezialisierte Aktionsfunktion ein. Diese Iterationsfunktionen iterieren die Sammlung und rufen die eingabebezogene Aktionsfunktion für jedes Element auf. Diese Aktionsfunktionen sind präziser, da sie die Schleifenanweisungen nicht mehr enthalten müssen, um die Auflistung zu iterieren.
Beachten Sie jedoch, dass eine Funktion, wenn sie nicht rein ist, tatsächlich eine Prozedur ist. Wir können vielleicht argumentieren, dass funktionale Programmierung, die unreine Funktionen verwendet, wirklich prozedurale Programmierung ist. Wenn wir uns also einig sind, dass deklarative Ausdrücke RT sind, können wir sagen, dass prozedurale Programmierung keine deklarative Programmierung ist, und daher könnten wir argumentieren, dass funktionale Programmierung immer RT ist und eine Teilmenge der deklarativen Programmierung sein muss.
Parallelität
Diese funktionale Zusammensetzung mit erstklassigen Funktionen kann die Tiefe der Parallelität ausdrücken, indem die unabhängige Funktion getrennt wird.
Brent-Prinzip: Die Berechnung mit Arbeit w und Tiefe d kann in einem p-Prozessor-PRAM in der Zeit O (max (w / p, d)) implementiert werden.
Woher kommt also diese gefährliche Annahme, dass Parallelität == Parallelität? Es ist eine natürliche Folge von Sprachen mit Nebenwirkungen: Wenn Ihre Sprache überall Nebenwirkungen hat, haben Sie jedes Mal, wenn Sie versuchen, mehr als eine Sache gleichzeitig zu tun, im Wesentlichen einen Nichtdeterminismus, der durch die Verschachtelung der Effekte aus jeder Operation verursacht wird . In Nebenwirkungssprachen ist die Parallelität der einzige Weg, um Parallelität zu erreichen. Es ist daher nicht verwunderlich, dass wir oft sehen, wie die beiden zusammenwachsen.
FP-Bewertungsreihenfolge
Beachten Sie, dass die Bewertungsreihenfolge auch die Abbruch- und Leistungsnebenwirkungen der funktionellen Zusammensetzung beeinflusst.
Eifrig (CBV) und faul (CBN) sind kategoriale Duelle [ 10 ], da sie eine umgekehrte Bewertungsreihenfolge haben, dh ob die äußeren oder inneren Funktionen zuerst bewertet werden. Stellen Sie sich einen umgedrehten Baum vor und werten Sie dann eifrig von Funktionszweigzweigen aus, die die Verzweigungshierarchie bis zum Funktionsstamm der obersten Ebene hochklappen. während faul vom Stamm bis zu den Astspitzen auswertet. Eifrig hat keine konjunktiven Produkte ("und", a / k / a kategoriale "Produkte") und faul hat keine disjunktiven Nebenprodukte ("oder", a / k / a kategoriale "Summen") [ 11 ].
Performance
Eifrig
Wie bei der Nichtbeendigung ist eifrig mit der konjunktiven funktionellen Zusammensetzung zu eifrig, dh die Kontrollstruktur der Komposition erledigt unnötige Arbeit, die nicht mit Faulheit erledigt wird. Zum Beispiel ordnet eifrig und unnötig die gesamte Liste Booleschen Werten zu, wenn sie mit einer Falte zusammengesetzt ist, die mit dem ersten wahren Element endet.
Diese unnötige Arbeit ist die Ursache für den behaupteten "bis zu" zusätzlichen log n-Faktor in der sequentiellen Zeitkomplexität von eifrig gegen faul, beide mit reinen Funktionen. Eine Lösung besteht darin, Funktoren (z. B. Listen) mit faulen Konstruktoren (dh eifrig mit optionalen faulen Produkten) zu verwenden, da mit Eifer die Eifersuchtfehler von der inneren Funktion herrühren. Dies liegt daran, dass Produkte konstruktive Typen sind, dh induktive Typen mit einer anfänglichen Algebra an einem anfänglichen Fixpunkt [ 11 ].
Faul
Wie bei der Nichtbeendigung ist Faulheit mit der disjunktiven funktionellen Zusammensetzung zu faul, dh die koinduktive Endgültigkeit kann später als nötig auftreten, was sowohl zu unnötiger Arbeit als auch zu Nichtdeterminismus der Verspätung führt, was bei Eifrigen nicht der Fall ist [ 10 ] [ 11 ]. . Beispiele für Endgültigkeit sind Status-, Timing-, Nichtbeendigungs- und Laufzeitausnahmen. Dies sind zwingende Nebenwirkungen, aber selbst in einer reinen deklarativen Sprache (z. B. Haskell) gibt es einen Zustand in der imperativen E / A-Monade (Anmerkung: Nicht alle Monaden sind zwingend erforderlich!), Der in der Raumzuweisung impliziert ist, und das Timing ist relativ zum Imperativ echte Welt. Die Verwendung von Faulheit auch bei optionalen eifrigen Nebenprodukten führt zu "Faulheit" in innere Nebenprodukte, da bei Faulheit die Faulheitsfehler von der äußeren Funktion herrühren(Siehe das Beispiel im Abschnitt Nichtbeendigung, wobei == eine äußere binäre Operatorfunktion ist.) Dies liegt daran, dass Nebenprodukte durch Endgültigkeit begrenzt sind, dh koinduktive Typen mit einer endgültigen Algebra für ein endgültiges Objekt [ 11 ].
Lazy verursacht Unbestimmtheit beim Entwerfen und Debuggen von Funktionen für Latenz und Speicherplatz, dessen Debuggen aufgrund der Dissonanz zwischen der deklarierten Funktionshierarchie und der Laufzeit-Evaluierungsreihenfolge wahrscheinlich über die Fähigkeiten der meisten Programmierer hinausgeht . Lazy Pure-Funktionen, die mit Eifer bewertet werden, können möglicherweise zur Laufzeit eine bisher nicht sichtbare Nichtbeendigung einführen. Umgekehrt könnten eifrige reine Funktionen, die mit Lazy bewertet werden, möglicherweise zur Laufzeit einen bisher nicht sichtbaren Indeterminismus für Speicherplatz und Latenz einführen.
Nichtbeendigung
Zur Kompilierungszeit kann aufgrund des Halteproblems und der gegenseitigen Rekursion in einer vollständigen Turing-Sprache nicht garantiert werden, dass Funktionen im Allgemeinen beendet werden.
Eifrig
Mit eifrig, aber nicht faul, für die Konjunktion von Head"und" Tail, wenn entweder Headoder Tailnicht endet, dann jeweils entweder List( Head(), Tail() ).tail == Tail()oder List( Head(), Tail() ).head == Head()nicht wahr, weil die linke Seite nicht und die rechte Seite endet.
Während mit Faulheit beide Seiten enden. Daher ist eifrig mit konjunktiven Produkten zu eifrig und endet nicht (einschließlich Laufzeitausnahmen) in den Fällen, in denen dies nicht erforderlich ist.
Faul
Mit faul, aber nicht eifrig, für die Disjunktion von 1"oder" 2, wenn fnicht endet, dann List( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tailist nicht wahr, weil die linke Seite endet und die rechte Seite nicht.
Während mit Eifer keine Seite endet, wird der Gleichheitstest nie erreicht. Daher ist faul mit faulen Nebenprodukten zu faul und kann in diesen Fällen nicht beendet werden (einschließlich Laufzeitausnahmen), nachdem mehr Arbeit geleistet wurde, als es eifrig gewesen wäre.
[ 10 ] Deklarative Fortsetzungen und kategoriale Dualität, Filinski, Abschnitte 2.5.4 Ein Vergleich von CBV und CBN sowie 3.6.1 CBV und CBN in der SCL.
[ 11 ] Deklarative Fortsetzungen und kategoriale Dualität, Filinski, Abschnitte 2.2.1 Produkte und Nebenprodukte, 2.2.2 End- und Ausgangsobjekte, 2.5.2 CBV mit faulen Produkten und 2.5.3 CBN mit eifrigen Nebenprodukten.
Selbst bei der deklarativen Einschränkungsprogrammierung mutieren die Einschränkungen nicht, während der Löser die Lösung findet. Dies ist offensichtlich, da es keine Möglichkeit gibt, einen Zeitpunkt für die Änderung anzugeben. Sogar Einschränkungen, die für andere Einschränkungen angegeben wurden, werden angegeben, bevor der Solver ausgeführt wird, um die Lösung zu finden. Dies ist analog zu den deklarativen Formeln in der Tabelle .
Shelby Moore III
3
Abkürzung bedeutet nicht, eine Definition anzugeben. Wo ich schrieb "RT wird oft als" keine Nebenwirkungen "abgekürzt", heißt das nicht, dass die Definition von RT "keine Nebenwirkungen" ist, da Menschen möglicherweise unterschiedliche Definitionen für "Effekte" haben. Wenn ich stattdessen sagte "RT wird oft mit 'xyz' abgekürzt", gibt ein bedeutungsloses Symbol RT keine Definition. RT hat eine genaue Definition , die sich nie ändert, egal auf welches Symbol man sich bezieht.
Shelby Moore III
Ich kann kein Gegenbeispiel zu meiner Behauptung finden, dass jede Art von DP RT ist. Beispielsweise mutiert die Bedeutung (dh der Wert) der Begriffe einer kontextsensitiven Grammatik nicht zu einem anderen Zeitpunkt oder an einer anderen Position innerhalb der Grammatik. Siehe meinen Kommentar zur Einschränkungsprogrammierung oben.
Shelby Moore III
1
Das Gleichsetzen von C im ESP-Stil mit RT in der Statusmonade ist ungültig , da jede C-Anweisung den globalen Status mutieren kann, während "innerhalb" der Statusmonade jede entsprechende Anweisung eine COPY des Status generiert (so modifiziert). Letzteres ist RT - Ersteres nicht. Monadische Zusammensetzung ist immer RT. DP == RT ist die einzige Bedeutung für DP, die eine disjunkte Menge von Attributen ist (der mathematische Beweis, dass ich richtig bin, sonst ist DP bedeutungslos).
Shelby Moore III
1
Ich wünschte, ich könnte dies nach dem ersten Satz verstehen. Ich habe das Handbuch für DAX gelesen, in dem angegeben wurde, dass es sich um eine "funktionale Sprache" handelt. Was bedeutet das? Ich weiß nicht, frag deinen Pop.
Nick.McDermaid
103
Es gibt keine wirklich mehrdeutige, objektive Definition für diese. So würde ich sie definieren:
Imperativ - Der Fokus liegt darauf, welche Schritte der Computer ausführen sollte und nicht darauf, was der Computer tun wird (z. B. C, C ++, Java).
Deklarativ - Der Fokus liegt darauf, was der Computer tun soll und nicht wie er es tun soll (z. B. SQL).
Funktional - eine Teilmenge deklarativer Sprachen, bei der die Rekursion im Vordergrund steht
Beachten Sie ein paar Dinge: 1) Die Erklärung soll eher einfach als allumfassend sein. 2) Wie ich bereits sagte, gibt es mehrere Möglichkeiten, diese Sprachen zu definieren. Daher könnte die Antwort sehr gut falsch für Sie und richtig für jemand anderen sein.
Jason Baker
3
Funktionale Programmierung ist keine "Teilmenge deklarativer Sprachen". Deklarative Programmierung erfordert die Unveränderlichkeit gespeicherter Werte, funktionale Programmierung nicht, wenn es sich nicht um reines FP handelt. Siehe meine Antwort . Siehe auch die Erklärung für Tabellenkalkulationszellen . Die korrekten objektiven Definitionen sind nicht "mehrdeutig". Die imperative Programmierung konzentriert sich auch darauf, "was der Computer tun soll". Der einzige Unterschied besteht darin, dass die zwingende Programmierung sich mit veränderlichen gespeicherten Werten befassen muss.
Shelby Moore III
5
@ShelbyMooreIII - Ich stimme Eric Meijer in diesem Punkt eher zu. Es gibt nicht wirklich eine "nicht reine funktionale Sprache". Für mich sind Ocaml, F # und dergleichen zwingende Sprachen mit funktionalen Datenstrukturen. Aber wie ich in meiner Antwort sagte, glaube ich nicht, dass es eine objektive, nicht mehrdeutige Antwort auf diese Frage gibt. Es gibt mehrere Möglichkeiten, Dinge zu definieren.
Jason Baker
3
Man kann mathematisch beweisen, dass er Begriffe zusammenführt, wenn keine der Definitionen eindeutig ist, weil die ausgewählten Attribute keine disjunkte Menge sind. Wenn Sie FP nur als reines FP (dh RT) definieren, unterscheidet es sich nicht von DP, vgl. meine Antwort . Zu den disjunkten Attributen von FP gehört der erstklassige Funktionstyp, der eine zwingende Funktion sein kann. Ich fand hier und hier grundlegendere Begriffe Mehrdeutigkeit . Die Bevorzugung von reinem FP ist orthogonal zur Definition von nur FP.
Shelby Moore III
21
@ShelbyMooreIII - Ich ging davon aus, dass das OP seine Antwort auf Englisch und nicht auf Math Nerd-ese haben wollte. Wenn das eine ungültige Annahme war, dann entschuldige ich mich.
Jason Baker
54
Imperativ und Deklarativ beschreiben zwei gegensätzliche Programmierstile. Imperativ ist der traditionelle "Schritt-für-Schritt-Rezept" -Ansatz, während deklarativ eher "das ist, was ich will, jetzt arbeiten Sie aus, wie es geht".
Diese beiden Ansätze treten während der gesamten Programmierung auf - auch mit derselben Sprache und demselben Programm. Im Allgemeinen wird der deklarative Ansatz als vorzuziehen angesehen, da er den Programmierer davon befreit, so viele Details angeben zu müssen, und gleichzeitig weniger Fehler auftreten kann (wenn Sie das gewünschte Ergebnis beschreiben und ein gut getesteter automatischer Prozess von dort bis rückwärts funktionieren kann Definieren Sie die Schritte, dann hoffen Sie vielleicht, dass die Dinge zuverlässiger sind, als jeden Schritt von Hand angeben zu müssen.
Auf der anderen Seite bietet ein zwingender Ansatz eine bessere Kontrolle auf niedriger Ebene - es ist der "Mikromanager-Ansatz" für die Programmierung. Dies kann es dem Programmierer ermöglichen, das Wissen über das Problem zu nutzen, um eine effizientere Antwort zu geben. Daher ist es nicht ungewöhnlich, dass einige Teile eines Programms in einem deklarativeren Stil geschrieben werden, aber dass die geschwindigkeitskritischen Teile zwingender sind.
Wie Sie sich vorstellen können, wirkt sich die Sprache, in der Sie ein Programm schreiben, darauf aus, wie deklarativ Sie sein können. Eine Sprache, in die "Smarts" integriert sind, um herauszufinden, was bei einer Beschreibung des Ergebnisses zu tun ist, ermöglicht eine viel deklarativere Sprache Ansatz als einer, bei dem der Programmierer zuerst diese Art von Intelligenz mit zwingendem Code hinzufügen muss, bevor er eine deklarativere Ebene darauf aufbauen kann. So wird beispielsweise eine Sprache wie Prolog als sehr deklarativ angesehen, da sie über einen integrierten Prozess verfügt, der nach Antworten sucht.
Bisher werden Sie feststellen, dass ich die funktionale Programmierung nicht erwähnt habe . Das liegt daran, dass es sich um einen Begriff handelt, dessen Bedeutung nicht unmittelbar mit den beiden anderen zusammenhängt. Im einfachsten Fall bedeutet funktionale Programmierung, dass Sie Funktionen verwenden. Insbesondere, dass Sie eine Sprache verwenden, die Funktionen als "erstklassige Werte" unterstützt - das bedeutet, dass Sie nicht nur Funktionen schreiben können, sondern auch Funktionen schreiben können, die Funktionen schreiben (die Funktionen schreiben, die ...), und Funktionen an übergeben Funktionen. Kurz gesagt - diese Funktionen sind so flexibel und allgemein wie Dinge wie Zeichenfolgen und Zahlen.
Es mag daher seltsam erscheinen, dass funktional, imperativ und deklarativ oft zusammen erwähnt werden. Der Grund dafür ist eine Folge der Idee der funktionalen Programmierung "auf das Äußerste". Eine Funktion ist im reinsten Sinne etwas aus der Mathematik - eine Art "Black Box", die Eingaben benötigt und immer die gleiche Ausgabe liefert. Für diese Art von Verhalten müssen sich ändernde Variablen nicht gespeichert werden. Wenn Sie also eine Programmiersprache entwerfen, deren Ziel es ist, eine sehr reine, mathematisch beeinflusste Idee der funktionalen Programmierung zu implementieren, lehnen Sie die Idee von Werten, die sich ändern können (in einem bestimmten, begrenzten technischen Sinne), weitgehend ab.
und wenn Sie dies tun - wenn Sie einschränken, wie sich Variablen ändern können -, zwingen Sie den Programmierer fast zufällig dazu, deklarativere Programme zu schreiben, da ein großer Teil der imperativen Programmierung beschreibt, wie sich Variablen ändern, und Sie dies nicht mehr können TU das! Es stellt sich also heraus, dass funktionale Programmierung - insbesondere Programmieren in einer funktionalen Sprache - tendenziell mehr deklarativen Code liefert.
um es zusammenzufassen:
Imperativ und Deklarativ sind zwei gegensätzliche Programmierstile (die gleichen Namen werden für Programmiersprachen verwendet, die diese Stile fördern).
Funktionale Programmierung ist ein Programmierstil, bei dem Funktionen sehr wichtig werden und infolgedessen sich ändernde Werte weniger wichtig werden. Die eingeschränkte Möglichkeit, Wertänderungen anzugeben, erzwingt einen deklarativeren Stil.
"funktionale Programmierung" wird daher oft als "deklarativ" bezeichnet.
Beste Erklärung bisher. Es scheint, dass Functional und OOP orthogonal zu Imperativ und Deklarativ sind.
Didier A.
Würden Sie sagen, dass die Logikprogrammierung deklarativ ist? Oder ist es selbst orthogonal?
Didier A.
51
In einer Nussschale:
Eine imperative Sprache gibt eine Reihe von Anweisungen an, die der Computer nacheinander ausführt (dies tun, dann das tun).
Eine deklarative Sprache deklariert eine Reihe von Regeln darüber, welche Ausgaben aus welchen Eingaben resultieren sollen (z. B. wenn Sie A haben, ist das Ergebnis B). Eine Engine wendet diese Regeln auf Eingaben an und gibt eine Ausgabe aus.
Eine funktionale Sprache deklariert eine Reihe von mathematisch / logischen Funktionen, die definieren, wie Eingabe in Ausgabe übersetzt wird. z.B. f (y) = y * y. Es ist eine Art deklarative Sprache.
Funktionale Programmierung ist keine "Art deklarativer Sprache". Deklarative Programmierung erfordert die Unveränderlichkeit gespeicherter Werte, unreine funktionale Programmierung nicht. Siehe meine Antwort . Siehe auch die Erklärung für Tabellenkalkulationszellen . Der einzige Grund, warum imperative Logik (auch Anweisungen genannt) nacheinander ausgeführt wird, besteht darin, dass das Ergebnis aufgrund des Vorhandenseins veränderlicher gespeicherter Werte von der Bewertungsreihenfolge abhängt. Mit Ihrem Wortschatz kann eine "Anweisung" veränderbare Werte verarbeiten (und eine "Regel" nicht).
Shelby Moore III
23
Imperativ: Wie erreichen wir unser Ziel ?
Take the next customer from a list.
If the customer lives in Spain, show their details.
If there are more customers in the list, go to the beginning
Deklarativ: Was wir erreichen wollen
Show customer details of every customer living in Spain
Sie beschreiben funktionale Programmierung vs. Nicht-FP, nicht deklarative vs. imperative Programmierung. Die funktionale Programmierung ist orthogonal zur Polarität zwischen imperativer und deklarativer Programmierung. Deklarative Programmierung erfordert die Unveränderlichkeit gespeicherter Werte, unreine funktionale Programmierung nicht. Siehe meine Antwort .
Shelby Moore III
22
Imperative Programmierung bezeichnet jede Art der Programmierung, bei der Ihr Programm aus Anweisungen besteht, die beschreiben, wie die von einem Computer ausgeführten Vorgänge ausgeführt werden .
Deklarative Programmierung bezeichnet jeden Programmierstil, bei dem Ihr Programm entweder eine Beschreibung des Problems oder der Lösung darstellt, jedoch nicht explizit angibt, wie die Arbeit ausgeführt wird .
Funktionale Programmierung ist Programmierung durch Bewertung von Funktionen und Funktionen von Funktionen ... Als (streng definierte) funktionale Programmierung bedeutet Programmierung durch Definition von nebenwirkungsfreien mathematischen Funktionen, so dass es sich um eine Form der deklarativen Programmierung handelt, aber nicht die einzige Art der deklarativen Programmierung .
Die Logikprogrammierung (zum Beispiel in Prolog) ist eine andere Form der deklarativen Programmierung. Dabei wird berechnet, ob eine logische Aussage wahr ist (oder ob sie erfüllt werden kann). Das Programm besteht normalerweise aus einer Reihe von Fakten und Regeln - dh eher einer Beschreibung als einer Reihe von Anweisungen.
Term Rewriting (zum Beispiel CASL) ist eine andere Form der deklarativen Programmierung. Es beinhaltet die symbolische Transformation algebraischer Begriffe. Es unterscheidet sich vollständig von der Logikprogrammierung und der Funktionsprogrammierung.
Funktionale Programmierung ist keine "Form der deklarativen Programmierung". Deklarative Programmierung erfordert die Unveränderlichkeit gespeicherter Werte, unreine funktionale Programmierung nicht. Siehe meine Antwort . Siehe auch die Erklärung für Tabellenkalkulationszellen . Der Begriff "Arbeit" in "Beschreibung der Arbeitsweise" ist nicht definiert. Der einzige Grund, warum imperative Logik (auch als "Anweisungen" bezeichnet) nacheinander ausgeführt wird, besteht darin, dass das Ergebnis aufgrund des Vorhandenseins veränderlicher gespeicherter Werte von der Bewertungsreihenfolge abhängt.
Shelby Moore III
2
Bitte nehmen Sie es als gelesen, dass ich über reine funktionale Programmierung gesprochen habe . Diese Paradigmen können sich überschneiden, und ich möchte mich nicht beim Vergleich hybrider Sprachen festsetzen. Theoretisch geht es zumindest bei der funktionalen Programmierung eher um Funktionen als darum zu beschreiben, wie ein Computer jede Berechnung ausführt - daher halte ich sie für deklarativ.
Dafydd Rees
Ich habe meine Antwort bearbeitet und im Abschnitt "Funktionale Programmierung" ein Szenario hinzugefügt, in dem wir argumentieren könnten, dass FP immer rein und unreines FP wirklich "prozedurale Programmierung" ist. Entschuldigung, dass Sie diese Interpretation nicht früher aufgenommen haben.
Shelby Moore III
13
Imperativ - Ausdrücke beschreiben die Abfolge der auszuführenden Aktionen (assoziativ)
deklarativ - Ausdrücke sind Deklarationen, die zum Verhalten des Programms beitragen (assoziativ, kommutativ, idempotent, monoton).
Funktionsausdrücke haben Wert als einzige Wirkung; Die Semantik unterstützt das Gleichungsdenken
Deklarative Ausdrücke tragen zum beabsichtigten Verhalten des Programms bei, Imperative können zu beabsichtigtem oder unbeabsichtigtem Verhalten beitragen. Deklarativ muss nicht kommutativ und idempotent sein, wenn dies absichtliche Semantik ist. Ich mag Ihre prägnante funktionale Essenz, deshalb habe ich sie positiv bewertet.
Shelby Moore III
10
Seit ich meine vorherige Antwort geschrieben habe, habe ich eine neue Definition der deklarativen Eigenschaft formuliert, die unten zitiert wird. Ich habe auch imperative Programmierung als die duale Eigenschaft definiert.
Diese Definition ist der in meiner vorherigen Antwort angegebenen überlegen, da sie prägnant und allgemeiner ist. Es kann jedoch schwieriger sein, etwas zu verstehen, da die Implikation der Unvollständigkeitssätze, die für die Programmierung und das Leben im Allgemeinen gelten, für den Menschen schwierig ist, sich Gedanken zu machen.
Die zitierte Erklärung der Definition diskutiert die Rolle der reinen funktionalen Programmierung bei der deklarativen Programmierung.
Alle exotischen Programmiertypen passen in die folgende Taxonomie von deklarativ gegenüber imperativ, da die folgende Definition behauptet, sie seien Duale.
Deklarativ vs. Imperativ
Die deklarative Eigenschaft ist seltsam, stumpf und schwer in einer technisch präzisen Definition zu erfassen, die allgemein und nicht mehrdeutig bleibt, da es eine naive Vorstellung ist, dass wir die Bedeutung (auch bekannt als Semantik) des Programms deklarieren können, ohne dass unbeabsichtigte Nebenwirkungen auftreten. Es gibt eine inhärente Spannung zwischen dem Ausdruck von Bedeutung und der Vermeidung unbeabsichtigter Effekte, und diese Spannung ergibt sich tatsächlich aus den Unvollständigkeitssätzen der Programmierung und unseres Universums.
Es ist zu einfach, technisch ungenau und oft nicht eindeutig, deklarativ als „ was zu tun ist “ und zwingend als „ wie zu tun “ zu definieren . Ein mehrdeutiger Fall ist das „ Was “ ist das „ Wie “ in einem Programm, das ein Programm ausgibt - einen Compiler.
Offensichtlich liegt die unbegrenzte Rekursion, die eine Sprache Turing vervollständigt , auch analog in der Semantik - nicht nur in der syntaktischen Struktur der Bewertung (auch bekannt als operative Semantik). Dies ist logischerweise ein Beispiel analog zu Gödels Theorem: „Jedes vollständige Axiomensystem ist auch inkonsistent. “ Denken Sie über die widersprüchliche Verrücktheit dieses Zitats nach! Es ist auch ein Beispiel, das zeigt, dass der Ausdruck der Semantik keine nachweisbare Grenze hat, daher können wir nicht beweisen 2, dass ein Programm (und analog seine Semantik), auch bekannt als Halting-Theorem, anhält.
Die Unvollständigkeitssätze leiten sich aus der fundamentalen Natur unseres Universums ab, die, wie im zweiten Hauptsatz der Thermodynamik angegeben, lautet: „ Die Entropie (auch bekannt als die Anzahl der unabhängigen Möglichkeiten) tendiert für immer zum Maximum “. Das Codieren und Entwerfen eines Programms ist nie abgeschlossen - es lebt! -, weil es versucht, ein Bedürfnis der realen Welt zu befriedigen, und die Semantik der realen Welt sich ständig ändert und zu mehr Möglichkeiten tendiert. Menschen hören nie auf, neue Dinge zu entdecken (einschließlich Fehler in Programmen ;-).
Um diesen oben erwähnten gewünschten Begriff innerhalb dieses seltsamen Universums, das keinen Rand hat, genau und technisch zu erfassen (denken Sie darüber nach! Es gibt kein „Äußeres“ unseres Universums), ist eine knappe, aber täuschend nicht einfache Definition erforderlich, die falsch klingt, bis sie erklärt wird tief.
Definition:
In der deklarativen Eigenschaft kann es nur einen möglichen Satz von Anweisungen geben, die jede spezifische modulare Semantik ausdrücken können.
Die imperative Eigenschaft 3 ist das Duale, bei dem die Semantik unter der Zusammensetzung inkonsistent ist und / oder mit Variationen von Aussagen ausgedrückt werden kann.
Diese Definition von deklarativ ist im semantischen Bereich eindeutig lokal , was bedeutet, dass eine modulare Semantik ihre konsistente Bedeutung beibehalten muss, unabhängig davon, wo und wie sie im globalen Bereich instanziiert und verwendet wird . Daher sollte jede deklarative modulare Semantik inhärent orthogonal zu allen möglichen anderen sein - und kein unmöglicher (aufgrund von Unvollständigkeitssätzen) globaler Algorithmus oder Modell für das Erleben von Konsistenz, was auch der Punkt von „ Mehr ist nicht immer besser “ von Robert Harper, Professor, ist of Computer Science an der Carnegie Mellon University, einem der Designer von Standard ML.
Beispiele für diese modulare deklarative Semantik sind kategorietheoretische Funktoren, z. B. dieApplicative nominale Typisierung, Namespaces, benannte Felder und die operative Funktion der Semantik, dann die reine funktionale Programmierung.
So können gut gestaltete deklarative Sprachen die Bedeutung klarer ausdrücken , wenn auch mit einem gewissen Verlust an Allgemeinheit in dem, was ausgedrückt werden kann, und einem Gewinn in dem, was mit intrinsischer Konsistenz ausgedrückt werden kann.
Ein Beispiel für die oben genannte Definition ist der Satz von Formeln in den Zellen eines Tabellenkalkulationsprogramms, von denen nicht erwartet wird, dass sie dieselbe Bedeutung haben, wenn sie in verschiedene Spalten- und Zeilenzellen verschoben werden, dh die Zellkennungen werden geändert. Die Zellkennungen sind Teil der beabsichtigten Bedeutung und nicht überflüssig. Daher ist jedes Tabellenkalkulationsergebnis für die Zellkennungen in einer Reihe von Formeln eindeutig. Die konsistente modulare Semantik ist in diesem Fall die Verwendung von Zellkennungen als Eingabe und Ausgabe von reinen Funktionen für Zellformeln (siehe unten).
Die Hyper Text Markup Language alias HTML - die Sprache für statische Webseiten - ist ein Beispiel für eine hochgradig (aber nicht perfekt 3 ) deklarative Sprache, die (zumindest vor HTML 5) kein dynamisches Verhalten ausdrücken konnte. HTML ist vielleicht die am einfachsten zu erlernende Sprache. Für dynamisches Verhalten wurde normalerweise eine zwingende Skriptsprache wie JavaScript mit HTML kombiniert. HTML ohne JavaScript passt zur deklarativen Definition, da jeder nominelle Typ (dh die Tags) seine konsistente Bedeutung unter Komposition innerhalb der Regeln der Syntax beibehält.
Eine konkurrierende Definition für deklarativ sind die kommutativen und idempotenten Eigenschaften der semantischen Anweisungen, dh, Anweisungen können neu geordnet und dupliziert werden, ohne die Bedeutung zu ändern. Beispielsweise können Anweisungen, die benannten Feldern Werte zuweisen, neu angeordnet und dupliziert werden, ohne die Bedeutung des Programms zu ändern, wenn diese Namen in einer impliziten Reihenfolge modular aufgebaut sind. Namen implizieren manchmal eine Reihenfolge, z. B. enthalten Zellkennungen ihre Spalten- und Zeilenposition. Wenn Sie eine Summe in der Tabelle verschieben, ändert sich ihre Bedeutung. Andernfalls erfordern diese Eigenschaften implizit globalKonsistenz der Semantik. Es ist im Allgemeinen unmöglich, die Semantik von Anweisungen so zu gestalten, dass sie konsistent bleiben, wenn sie zufällig angeordnet oder dupliziert werden, da Reihenfolge und Duplizierung der Semantik eigen sind. Zum Beispiel die Aussagen "Foo existiert" (oder Konstruktion) und "Foo existiert nicht" (und Zerstörung). Wenn man zufällige Inkonsistenzen als endemisch für die beabsichtigte Semantik betrachtet, akzeptiert man diese Definition als allgemein genug für die deklarative Eigenschaft. Im Wesentlichen ist diese Definition als verallgemeinerte Definition leer, weil sie versucht, die Konsistenz orthogonal zur Semantik zu machen, dh der Tatsache zu trotzen, dass das Universum der Semantik dynamisch unbegrenzt ist und nicht in einem globalen Kohärenzparadigma erfasst werden kann .
Das Erfordernis der kommutativen und idempotenten Eigenschaften für die (strukturelle Bewertungsreihenfolge der) Betriebssemantik der unteren Ebene konvertiert die Betriebssemantik in eine deklarative lokalisierte modulare Semantik, z. B. reine funktionale Programmierung (einschließlich Rekursion anstelle von Imperativschleifen). Dann wirkt sich die Betriebsreihenfolge der Implementierungsdetails nicht auf die Konsistenz der übergeordneten Semantik aus (dh global verteilt ). Zum Beispiel spielt die Reihenfolge der Auswertung (und theoretisch auch der Vervielfältigung) der Tabellenkalkulationsformeln keine Rolle, da die Ausgaben erst in die Eingaben kopiert werden, nachdem alle Ausgaben berechnet wurden, dh analog zu reinen Funktionen.
C, Java, C ++, C #, PHP und JavaScript sind nicht besonders deklarativ. Die Syntax von Copute und die Syntax von Python sind deklarativer an die beabsichtigten Ergebnisse gekoppelt , dh an eine konsistente syntaktische Semantik, die das Fremde eliminiert, sodass man Code leicht verstehen kann, nachdem man ihn vergessen hat. Copute und Haskell erzwingen den Determinismus der operativen Semantik und ermutigen dazu, sich nicht zu wiederholen (DRY), weil sie nur das reine Funktionsparadigma zulassen.
2 Selbst wenn wir die Semantik eines Programms beweisen können, z. B. mit der Sprache Coq, ist dies auf die Semantik beschränkt, die in der Eingabe ausgedrückt wird , und die Eingabe kann niemals die gesamte Semantik eines Programms erfassen - nicht einmal für Sprachen, die es sind nicht vollständig, z. B. mit HTML + CSS, ist es möglich, inkonsistente Kombinationen auszudrücken, die somit eine undefinierte Semantik haben.
3 Viele Erklärungen behaupten fälschlicherweise, dass nur die imperative Programmierung syntaktisch geordnete Anweisungen enthält. Ich habe diese Verwechslung zwischen imperativer und funktionaler Programmierung geklärt . Beispielsweise verringert die Reihenfolge der HTML-Anweisungen nicht die Konsistenz ihrer Bedeutung.
Bearbeiten: Ich habe den folgenden Kommentar in Robert Harpers Blog gepostet :
in der funktionalen Programmierung ... ist der Variationsbereich einer Variablen ein Typ
Abhängig davon, wie man funktionale von imperativer Programmierung unterscheidet, kann Ihr 'zuweisbar' in einem imperativen Programm auch einen Typ haben, der seine Variabilität begrenzt.
Die einzige nicht durcheinandergebrachte Definition, die ich derzeit für die funktionale Programmierung schätze, ist a) Funktionen als erstklassige Objekte und Typen, b) Präferenz für Rekursion gegenüber Schleifen und / oder c) reine Funktionen - dh solche Funktionen, die die gewünschte Semantik nicht beeinflussen des Programms, wenn es gespeichert ist ( daher existiert aufgrund der Auswirkungen der Betriebssemantik, z . B. der Speicherzuweisung, keine vollkommen reine funktionale Programmierung in einer allgemeinen Denotationssemantik ).
Die idempotente Eigenschaft einer reinen Funktion bedeutet, dass der Funktionsaufruf für ihre Variablen durch ihren Wert ersetzt werden kann, was bei den Argumenten einer imperativen Prozedur im Allgemeinen nicht der Fall ist. Reine Funktionen scheinen für die nicht zusammengesetzten Zustandsübergänge zwischen den Eingabe- und Ergebnistypen deklarativ zu sein.
Die Zusammensetzung reiner Funktionen behält jedoch keine solche Konsistenz bei, da es möglich ist, einen Nebenwirkungsprozess (globaler Zustand) in einer rein funktionalen Programmiersprache, z. B. Haskells IOMonad, zu modellieren, und darüber hinaus ist es völlig unmöglich, dies zu verhindern jede Turing komplett reine funktionale Programmiersprache.
Wie ich 2012 schrieb, was dem ähnlichen Konsens der Kommentare in Ihrem letzten Blog zu entsprechen scheint , ist diese deklarative Programmierung ein Versuch, die Vorstellung zu erfassen, dass die beabsichtigte Semantik niemals undurchsichtig ist. Beispiele für undurchsichtige Semantik sind die Abhängigkeit von der Ordnung, die Abhängigkeit vom Löschen der Semantik höherer Ebenen auf der Ebene der operativen Semantik (z. B. Casts sind keine Konvertierungen und reifizierte Generika begrenzen die Semantik höherer Ebenen ) und die Abhängigkeit von variablen Werten, die nicht überprüft werden können (bewiesen) richtig) durch die Programmiersprache.
Daher bin ich zu dem Schluss gekommen, dass nur nicht-Turing-vollständige Sprachen deklarativ sein können.
Ein eindeutiges und eindeutiges Merkmal einer deklarativen Sprache könnte daher sein, dass nachgewiesen werden kann, dass ihre Ausgabe einer Reihe von generativen Regeln entspricht. Beispielsweise kann für ein bestimmtes HTML-Programm (das Unterschiede in der Art und Weise, wie Interpreter voneinander abweichen) ignoriert wird, das nicht mit Skripten versehen ist (dh nicht vollständig ist), die Ausgabevariabilität aufzählbar sein. Kurz gesagt, ein HTML-Programm ist eine reine Funktion seiner Variabilität. Ebenso ist ein Tabellenkalkulationsprogramm eine reine Funktion seiner Eingabevariablen.
Es scheint mir also, dass deklarative Sprachen das Gegenteil einer
unbegrenzten Rekursion sind , dh nach Gödels zweitem Unvollständigkeitssatz können selbstreferenzielle Theoreme nicht bewiesen werden.
Lesie Lamport schrieb ein Märchen darüber, wie Euklid Gödels Unvollständigkeitssätze, die auf mathematische Beweise im Kontext der Programmiersprache angewendet wurden, durch Kongruenz zwischen Typen und Logik (Curry-Howard-Korrespondenz usw.) umgangen haben könnte.
gut, aber besser, wenn Sie mindestens ein Beispiel für beide angeben!
Pardeep Jain
4
Heutzutage neuer Fokus: Wir brauchen die alten Klassifikationen?
Die imperativen / deklarativen / funktionalen Aspekte waren in der Vergangenheit gut, um generische Sprachen zu klassifizieren, aber heutzutage haben alle "großen Sprachen" (wie Java, Python, Javascript usw.) eine Option (normalerweise Frameworks ), um sie mit "anderem Fokus" auszudrücken. als sein Haupt (üblicher Imperativ), und parallele Prozesse, deklarative Funktionen, Lambdas usw. auszudrücken.
Eine gute Variante dieser Frage lautet also: "Welcher Aspekt ist heute gut, um Frameworks zu klassifizieren?"
... Ein wichtiger Aspekt ist etwas, das wir als "Programmierstil" bezeichnen können ...
Konzentrieren Sie sich auf die Fusion von Daten mit Algorithmus
Die von der Selector Engine (...) aktivierten jQuery-Kernfunktionen - Auswahl, Durchquerung und Manipulation von DOM-Elementen - haben einen neuen "Programmierstil" geschaffen, der Algorithmen und DOM-Datenstrukturen zusammenführt
So jQuery ist das beste (populär) Beispiel auf einen der Fokussierung „neuen Programmierstil“ , das Objekt nicht nur Orientierung ist, sind „ Fusing Algorithmen und Datenstrukturen “. jQuery ist etwas reaktiv wie Tabellen, aber nicht „Zelle orientiert“, ist „ DOM-Knoten orientiert “ ... die wichtigsten Vergleicht man Stile in diesem Zusammenhang:
Keine Verschmelzung : In allen "großen Sprachen", in jedem funktionalen / deklarativen / imperativen Ausdruck, ist das Übliche "keine Verschmelzung" von Daten und Algorithmen, außer durch eine gewisse Objektorientierung, dh eine Verschmelzung unter strengen Gesichtspunkten der algebrischen Struktur .
Einige Fusionen : Alle klassischen Fusionsstrategien haben heutzutage einen Rahmen, der sie als Paradigma verwendet ... Datenfluss , ereignisgesteuerte Programmierung (oder alte domänenspezifische Sprachen wie awk und XSLT ) ... Wie die Programmierung mit modernen Tabellenkalkulationen auch Beispiele für reaktiven Programmierstil .
Big Fusion : „der jQuery - Stil“ ... jQuery ist eine domänenspezifische Sprache mit Schwerpunkt auf „ Verschmelzen Algorithmen und DOM-Daten-Strukturen “. PS: Andere "Abfragesprachen" wie XQuery, SQL (mit PL als Option für zwingende Ausdrücke) sind ebenfalls Beispiele für die Fusion von Datenalgorithmen , aber es handelt sich um Inseln ohne Fusion mit anderen Systemmodulen ... Spring , wenn find()-varianten verwendet werden und Spezifikationsklauseln ist ein weiteres gutes Fusionsbeispiel.
Deklarative Programmierung ist Programmierung, indem eine zeitlose Logik zwischen dem Eingang und dem Ausgang ausgedrückt wird, beispielsweise im Pseudocode. Das folgende Beispiel wäre deklarativ:
def factorial(n):
if n < 2:
return 1
else:
return factorial(n-1)
output = factorial(argvec[0])
Wir definieren hier nur eine Beziehung, die als "Fakultät" bezeichnet wird, und definieren die Beziehung zwischen der Ausgabe und der Eingabe als diese Beziehung. Wie hier offensichtlich sein sollte, erlaubt jede strukturierte Sprache in gewissem Umfang eine deklarative Programmierung. Eine zentrale Idee der deklarativen Programmierung sind unveränderliche Daten. Wenn Sie einer Variablen zuweisen, tun Sie dies nur einmal und dann nie wieder. Andere, strengere Definitionen haben zur Folge, dass möglicherweise überhaupt keine Nebenwirkungen auftreten. Diese Sprachen werden manchmal als „rein deklarativ“ bezeichnet.
Das gleiche Ergebnis in einem imperativen Stil wäre:
a = 1
b = argvec[0]
while(b < 2):
a * b--
output = a
In diesem Beispiel haben wir keine zeitlose statische logische Beziehung zwischen der Eingabe und der Ausgabe ausgedrückt. Wir haben die Speicheradressen manuell geändert, bis eine von ihnen das gewünschte Ergebnis enthielt. Es sollte offensichtlich sein, dass alle Sprachen eine deklarative Semantik in gewissem Umfang zulassen, aber nicht alle eine imperative, einige "rein" deklarative Sprachen erlauben Nebenwirkungen und Mutationen insgesamt.
In deklarativen Sprachen wird oft gesagt, dass sie angeben, was zu tun ist, im Gegensatz zu "wie es zu tun ist". Ich denke, das ist eine Fehlbezeichnung. Deklarative Programme geben immer noch an, wie man von der Eingabe zur Ausgabe gelangen muss, aber auf andere Weise die Die von Ihnen angegebene Beziehung muss effektiv berechenbar sein (wichtiger Begriff, suchen Sie nach, wenn Sie ihn nicht kennen). Ein anderer Ansatz ist die nichtdeterministische Programmierung, die wirklich nur angibt, welche Bedingungen ein Ergebnis erfüllt, bevor Ihre Implementierung alle Pfade für Versuch und Irrtum erschöpft, bis sie erfolgreich ist.
Zu den rein deklarativen Sprachen gehören Haskell und Pure Prolog. Eine gleitende Skala von einem zum anderen wäre: Reiner Prolog, Haskell, OCaml, Schema / Lisp, Python, Javascript, C--, Perl, PHP, C ++, Pascall, C, Fortran, Assembly
Sie haben keine funktionale Programmierung definiert. Sie haben fälschlicherweise "einige 'rein' deklarative Sprachen" impliziert, dass deklarative Programmierung unrein sein kann . Deklarative Programmierung erfordert die Unveränderlichkeit gespeicherter Werte, imperative Programmierung nicht. Siehe meine Antwort . Unveränderlichkeit ist die "zeitlose" Qualität - achten Sie darauf, dass Ihr Deklarativ factorialkeinen Wert mutiert.
Shelby Moore III
3
Einige gute Antworten hier zu den angegebenen "Typen".
Ich reiche einige zusätzliche, "exotischere" Konzepte ein, die häufig mit der Menge der funktionalen Programmierer verbunden sind:
Domänenspezifische Sprache oder DSL- Programmierung: Erstellen einer neuen Sprache zur Behebung des vorliegenden Problems.
Meta-Programmierung : Wenn Ihr Programm andere Programme schreibt.
Evolutionäre Programmierung : Hier erstellen Sie ein System, das sich kontinuierlich verbessert oder sukzessive bessere Generationen von Unterprogrammen generiert.
Ich denke, dass Ihre Taxonomie falsch ist. Es gibt zwei entgegengesetzte Typen: Imperativ und Deklarativ. Funktional ist nur ein Untertyp von deklarativ. Übrigens gibt Wikipedia die gleiche Tatsache an.
FP ist nicht "nur ein deklarativer Subtyp". FP ist orthogonal zur Polarität von Imperativ vs. DP. DP erfordert die Unveränderlichkeit gespeicherter Werte, unreines FP nicht. Wikipedia verbindet reines FP mit FP, mit der absurden Behauptung, dass die folgenden Konzepte "der imperativen Programmierung im Allgemeinen fremd" sind: erstklassige Funktionen, Rekursion, Auswertungsreihenfolge und statische Typisierung. Dann gibt Wikipedia unreine "funktionale Programmierung in nicht funktionalen Sprachen" zu.
Shelby Moore III
Wikipedia ist in diesem Punkt falsch. Viele gängige Funktionssprachen ermöglichen die Programmierung in einem "deklarativen Stil", wenn Sie dies wünschen, sind jedoch keine deklarativen Sprachen. Das Gleiche gilt für C, wo Sie bei Bedarf mit void * s immer noch in einem funktionalen Stil programmieren können.
Plynx
Wahrscheinlich hätte ich in diesem Punkt klarer sein sollen, aber von der anderen Seite würde ich den Themenstarter nicht mit nicht ganz relevanten (imo) Details durcheinander bringen. Ich sehe, dass funktionale Sprachen eher deklarativ verwendet werden. Sie können versuchen, deklarativ und / oder funktional in ASM oder C zu schreiben, oder Sie können wahrscheinlich ein imperatives Programm in Lisp schreiben, aber ich bezweifle, dass es für den Autor der Frage sehr hilfreich oder informativ wäre. Daher halte ich meine Antwort im Wesentlichen immer noch für angemessen, auch wenn sie anders formuliert sein könnte.
Rorick
2
Kurz gesagt, je mehr ein Programmierstil betont, was (zu tun) ist, um die Details von Wie (zu tun) zu abstrahieren, desto mehr wird dieser Stil als deklarativ angesehen. Das Gegenteil gilt für den Imperativ. Die funktionale Programmierung ist dem deklarativen Stil zugeordnet.
Siehe meine Kommentare unter den anderen Antworten. FP ist nicht immer deklarativ. Was gegen wie ist die falsche Taxonomie für IP gegen DP, da sowohl DP als auch IP eine Logik haben, die das Was und Wie beinhaltet.
Antworten:
Zum Zeitpunkt des Schreibens sind die Antworten mit den höchsten Stimmen auf dieser Seite ungenau und durcheinander in der deklarativen vs. imperativen Definition, einschließlich der Antwort, die Wikipedia zitiert. Einige Antworten verbinden die Begriffe auf unterschiedliche Weise.
Lesen Sie auch meine Erklärung, warum die Tabellenkalkulationsprogrammierung deklarativ ist, unabhängig davon, ob die Formeln die Zellen mutieren.
In mehreren Antworten wird auch behauptet, dass die funktionale Programmierung eine Teilmenge der deklarativen sein muss. Von diesem Punkt hängt es ab, ob wir "Funktion" von "Prozedur" unterscheiden. Behandeln wir zuerst Imperativ vs. Deklarativ.
Definition des deklarativen Ausdrucks
Das einzige Attribut, das einen deklarativen Ausdruck möglicherweise von einem imperativen Ausdruck unterscheiden kann, ist die referenzielle Transparenz (RT) seiner Unterausdrücke. Alle anderen Attribute werden entweder von beiden Ausdruckstypen gemeinsam genutzt oder von der RT abgeleitet.
Eine zu 100% deklarative Sprache (dh eine, in der jeder mögliche Ausdruck RT ist) erlaubt (neben anderen RT-Anforderungen) nicht die Mutation gespeicherter Werte, z. B. HTML und den größten Teil von Haskell.
Definition der RT-Expression
RT wird oft als "ohne Nebenwirkungen" bezeichnet. Der Begriff Effekte hat keine genaue Definition, daher stimmen einige Leute nicht darin überein, dass "keine Nebenwirkungen" mit RT identisch sind. RT hat eine genaue Definition .
Da jeder Unterausdruck konzeptionell ein Funktionsaufruf ist, erfordert RT, dass die Implementierung einer Funktion (dh der Ausdruck (der Ausdrücke) innerhalb der aufgerufenen Funktion) möglicherweise nicht auf den veränderlichen Zustand zugreift, der außerhalb der Funktion liegt (der Zugriff auf den veränderlichen lokalen Zustand ist) dürfen). Einfach ausgedrückt sollte die Funktion (Implementierung) rein sein .
Definition der reinen Funktion
Eine reine Funktion soll oft "keine Nebenwirkungen" haben. Der Begriff Effekte hat keine genaue Definition, daher stimmen einige Leute nicht zu.
Reine Funktionen haben die folgenden Attribute.
Denken Sie daran, dass RT für Ausdrücke (einschließlich Funktionsaufrufe) und Reinheit für (Implementierungen von) Funktionen gilt.
Ein obskures Beispiel für unreine Funktionen, die RT-Ausdrücke erzeugen, ist die Parallelität. Dies liegt jedoch daran, dass die Reinheit auf der Interrupt-Abstraktionsschicht unterbrochen ist. Das musst du nicht wirklich wissen. Um RT-Ausdrücke zu erstellen, rufen Sie reine Funktionen auf.
Abgeleitete Attribute von RT
Jedes andere für die deklarative Programmierung angeführte Attribut, z. B. das von Wikipedia verwendete Zitat aus dem Jahr 1999 , stammt entweder von RT oder wird mit der imperativen Programmierung geteilt. Dies beweist, dass meine genaue Definition korrekt ist.
Beachten Sie, dass die Unveränderlichkeit externer Werte eine Teilmenge der Anforderungen für RT ist.
Deklarative Sprachen haben keine Kontrollstrukturen Looping, zB
for
undwhile
, weil aufgrund Unveränderlichkeit , die Schleifenbedingung würde nie ändern.Deklarative Sprachen drücken keinen anderen Kontrollfluss als die verschachtelte Funktionsreihenfolge (auch als logische Abhängigkeiten bezeichnet) aus, da andere Optionen der Bewertungsreihenfolge aufgrund der Unveränderlichkeit das Ergebnis nicht ändern (siehe unten).
Deklarative Sprachen drücken logische "Schritte" aus (dh die verschachtelte Reihenfolge der RT-Funktionsaufrufe), aber ob jeder Funktionsaufruf eine Semantik höherer Ebene ist (dh "was zu tun ist"), ist keine Voraussetzung für die deklarative Programmierung. Der Unterschied zum Imperativ besteht darin, dass diese "Schritte" aufgrund der Unveränderlichkeit (dh allgemeiner RT) nicht vom veränderlichen Zustand abhängen können, sondern nur von der relationalen Reihenfolge der ausgedrückten Logik (dh der Reihenfolge der Verschachtelung der Funktionsaufrufe, auch Unterausdrücke genannt) ).
Beispielsweise kann der HTML-Absatz
<p>
erst angezeigt werden, wenn die Unterausdrücke (dh Tags) im Absatz ausgewertet wurden. Es gibt keinen veränderlichen Zustand, nur eine Ordnungsabhängigkeit aufgrund der logischen Beziehung der Tag-Hierarchie (Verschachtelung von Unterausdrücken, die analog verschachtelte Funktionsaufrufe sind ).Somit gibt es das abgeleitete Attribut der Unveränderlichkeit (allgemeiner RT), dass deklarative Ausdrücke nur die logischen Beziehungen der Bestandteile (dh der Argumente der Unterausdrucksfunktion) und nicht veränderbare Zustandsbeziehungen ausdrücken .
Auswertungsreihenfolge
Die Wahl der Auswertungsreihenfolge von Unterausdrücken kann nur dann zu einem variierenden Ergebnis führen, wenn einer der Funktionsaufrufe nicht RT ist (dh die Funktion ist nicht rein), z. B. wird innerhalb der Funktion auf einen veränderlichen Zustand außerhalb einer Funktion zugegriffen.
Zum Beispiel einiger verschachtelten Ausdrücke, zum Beispiel gegeben
f( g(a, b), h(c, d) )
, eifrig und faul Auswertung der Argumente Funktion der gleichen Ergebnisse liefern , wenn die Funktionenf
,g
undh
rein sind.Wenn dagegen die Funktionen
f
,g
undh
kann nicht rein ist , dann ist die Wahl der Bewertungs um ein anderes Ergebnis geben.Beachten Sie, dass verschachtelte Ausdrücke konzeptionell verschachtelte Funktionen sind, da Ausdrucksoperatoren nur Funktionsaufrufe sind, die sich als unäres Präfix, unäres Postfix oder binäre Infixnotation tarnen.
Tangential, wenn alle Bezeichner, zum Beispiel
a
,b
,c
,d
sind, unveränderlich überall, Zustand außerhalb des Programms nicht zugegriffen werden kann (dh I / O), und es gibt keine Abstraktionsschicht Bruch, dann sind Funktionen immer rein.Haskell hat übrigens eine andere Syntax
f (g a b) (h c d)
.Details zur Auswertungsreihenfolge
Eine Funktion ist ein Zustandsübergang (kein veränderlicher gespeicherter Wert) vom Eingang zum Ausgang. Für RT-Kompositionen von Aufrufen von reinen Funktionen ist die Ausführungsreihenfolge dieser Zustandsübergänge unabhängig. Der Zustandsübergang jedes Funktionsaufrufs ist aufgrund fehlender Nebenwirkungen und des Prinzips, dass eine RT-Funktion durch ihren zwischengespeicherten Wert ersetzt werden kann, unabhängig von den anderen . Um ein weit verbreitetes Missverständnis zu korrigieren , ist die reine monadische Komposition immer deklarativ und RT , trotz der Tatsache, dass Haskells
IO
Monade wohl unrein und daher für denWorld
Zustand außerhalb des Programms unabdingbar ist (aber im Sinne der Einschränkung unten die Nebenwirkungen) isoliert sind).Eifrige Auswertung bedeutet, dass die Funktionsargumente ausgewertet werden, bevor die Funktion aufgerufen wird, und verzögerte Auswertung bedeutet, dass die Argumente erst ausgewertet werden, wenn (und wenn) innerhalb der Funktion auf sie zugegriffen wird.
Definition : Funktionsparameter werden in der Funktion deklariert Definition Standort und Funktionsargumente werden an der Funktion geliefert Anruf Ort. Kennen Sie den Unterschied zwischen Parameter und Argument .
Konzeptionell alle Ausdrücke sind (eine Zusammensetzung) Funktionsaufrufe, z Konstanten sind Funktionen ohne Eingänge, unäre Operatoren sind Funktionen mit einem Eingang, binary Infixoperatoren sind Funktionen mit zwei Eingängen, Konstruktoren sind Funktionen, und auch Steueranweisungen (z
if
,for
,while
) kann mit Funktionen modelliert werden. Die Reihenfolge , dass diese Argumentation Funktionen (mit verschachteltem Funktionsaufruf , um nicht zu verwechseln tun) ausgewertet wird nicht durch die Syntax erklärt, zum Beispielf( g() )
könnte eifrig bewertetg
dannf
aufg
‚s Ergebnis oder es könnte bewertenf
und nur träge bewerten ,g
wenn das Ergebnis innerhalb benötigtf
.Vorbehalt, keine vollständige Turing- Sprache (dh die eine unbegrenzte Rekursion ermöglicht) ist vollkommen deklarativ, z. B. führt eine träge Auswertung Gedächtnis- und Zeitindeterminismus ein. Diese Nebenwirkungen aufgrund der Wahl der Auswertungsreihenfolge sind jedoch auf den Speicherverbrauch, die Ausführungszeit, die Latenz, die Nichtbeendigung und die externe Hysterese und damit auf die externe Synchronisation beschränkt.
Funktionsprogrammierung
Da deklarative Programmierung keine Schleifen haben kann, ist die einzige Möglichkeit zum Iterieren die funktionale Rekursion. In diesem Sinne bezieht sich die funktionale Programmierung auf die deklarative Programmierung.
Die funktionale Programmierung ist jedoch nicht auf die deklarative Programmierung beschränkt . Die funktionale Zusammensetzung kann der Subtypisierung gegenübergestellt werden , insbesondere im Hinblick auf das Expressionsproblem , bei dem die Erweiterung entweder durch Hinzufügen von Subtypen oder durch funktionale Zerlegung erreicht werden kann . Die Erweiterung kann eine Mischung aus beiden Methoden sein.
Die funktionale Programmierung macht die Funktion normalerweise zu einem erstklassigen Objekt, was bedeutet, dass der Funktionstyp in der Grammatik überall dort erscheinen kann, wo es einen anderen Typ gibt. Das Ergebnis ist, dass Funktionen Funktionen eingeben und bearbeiten können, wodurch eine Trennung von Bedenken durch Hervorheben der Funktionszusammensetzung erreicht wird, dh die Abhängigkeiten zwischen den Teilberechnungen einer deterministischen Berechnung getrennt werden.
Anstatt beispielsweise eine separate Funktion zu schreiben (und anstelle von Schleifen eine Rekursion zu verwenden, wenn die Funktion auch deklarativ sein muss), für jede einer unendlichen Anzahl möglicher spezialisierter Aktionen, die auf jedes Element einer Sammlung angewendet werden könnten, verwendet die funktionale Programmierung eine wiederverwendbare Iteration Funktionen, zB
map
,fold
,filter
. Diese Iterationsfunktionen geben eine erstklassige spezialisierte Aktionsfunktion ein. Diese Iterationsfunktionen iterieren die Sammlung und rufen die eingabebezogene Aktionsfunktion für jedes Element auf. Diese Aktionsfunktionen sind präziser, da sie die Schleifenanweisungen nicht mehr enthalten müssen, um die Auflistung zu iterieren.Beachten Sie jedoch, dass eine Funktion, wenn sie nicht rein ist, tatsächlich eine Prozedur ist. Wir können vielleicht argumentieren, dass funktionale Programmierung, die unreine Funktionen verwendet, wirklich prozedurale Programmierung ist. Wenn wir uns also einig sind, dass deklarative Ausdrücke RT sind, können wir sagen, dass prozedurale Programmierung keine deklarative Programmierung ist, und daher könnten wir argumentieren, dass funktionale Programmierung immer RT ist und eine Teilmenge der deklarativen Programmierung sein muss.
Parallelität
Diese funktionale Zusammensetzung mit erstklassigen Funktionen kann die Tiefe der Parallelität ausdrücken, indem die unabhängige Funktion getrennt wird.
Sowohl Parallelität als auch Parallelität erfordern auch deklarative Programmierung , dh Unveränderlichkeit und RT.
FP-Bewertungsreihenfolge
Beachten Sie, dass die Bewertungsreihenfolge auch die Abbruch- und Leistungsnebenwirkungen der funktionellen Zusammensetzung beeinflusst.
Eifrig (CBV) und faul (CBN) sind kategoriale Duelle [ 10 ], da sie eine umgekehrte Bewertungsreihenfolge haben, dh ob die äußeren oder inneren Funktionen zuerst bewertet werden. Stellen Sie sich einen umgedrehten Baum vor und werten Sie dann eifrig von Funktionszweigzweigen aus, die die Verzweigungshierarchie bis zum Funktionsstamm der obersten Ebene hochklappen. während faul vom Stamm bis zu den Astspitzen auswertet. Eifrig hat keine konjunktiven Produkte ("und", a / k / a kategoriale "Produkte") und faul hat keine disjunktiven Nebenprodukte ("oder", a / k / a kategoriale "Summen") [ 11 ].
Performance
Eifrig
Wie bei der Nichtbeendigung ist eifrig mit der konjunktiven funktionellen Zusammensetzung zu eifrig, dh die Kontrollstruktur der Komposition erledigt unnötige Arbeit, die nicht mit Faulheit erledigt wird. Zum Beispiel ordnet eifrig und unnötig die gesamte Liste Booleschen Werten zu, wenn sie mit einer Falte zusammengesetzt ist, die mit dem ersten wahren Element endet.
Diese unnötige Arbeit ist die Ursache für den behaupteten "bis zu" zusätzlichen log n-Faktor in der sequentiellen Zeitkomplexität von eifrig gegen faul, beide mit reinen Funktionen. Eine Lösung besteht darin, Funktoren (z. B. Listen) mit faulen Konstruktoren (dh eifrig mit optionalen faulen Produkten) zu verwenden, da mit Eifer die Eifersuchtfehler von der inneren Funktion herrühren. Dies liegt daran, dass Produkte konstruktive Typen sind, dh induktive Typen mit einer anfänglichen Algebra an einem anfänglichen Fixpunkt [ 11 ].
Faul
Wie bei der Nichtbeendigung ist Faulheit mit der disjunktiven funktionellen Zusammensetzung zu faul, dh die koinduktive Endgültigkeit kann später als nötig auftreten, was sowohl zu unnötiger Arbeit als auch zu Nichtdeterminismus der Verspätung führt, was bei Eifrigen nicht der Fall ist [ 10 ] [ 11 ]. . Beispiele für Endgültigkeit sind Status-, Timing-, Nichtbeendigungs- und Laufzeitausnahmen. Dies sind zwingende Nebenwirkungen, aber selbst in einer reinen deklarativen Sprache (z. B. Haskell) gibt es einen Zustand in der imperativen E / A-Monade (Anmerkung: Nicht alle Monaden sind zwingend erforderlich!), Der in der Raumzuweisung impliziert ist, und das Timing ist relativ zum Imperativ echte Welt. Die Verwendung von Faulheit auch bei optionalen eifrigen Nebenprodukten führt zu "Faulheit" in innere Nebenprodukte, da bei Faulheit die Faulheitsfehler von der äußeren Funktion herrühren(Siehe das Beispiel im Abschnitt Nichtbeendigung, wobei == eine äußere binäre Operatorfunktion ist.) Dies liegt daran, dass Nebenprodukte durch Endgültigkeit begrenzt sind, dh koinduktive Typen mit einer endgültigen Algebra für ein endgültiges Objekt [ 11 ].
Lazy verursacht Unbestimmtheit beim Entwerfen und Debuggen von Funktionen für Latenz und Speicherplatz, dessen Debuggen aufgrund der Dissonanz zwischen der deklarierten Funktionshierarchie und der Laufzeit-Evaluierungsreihenfolge wahrscheinlich über die Fähigkeiten der meisten Programmierer hinausgeht . Lazy Pure-Funktionen, die mit Eifer bewertet werden, können möglicherweise zur Laufzeit eine bisher nicht sichtbare Nichtbeendigung einführen. Umgekehrt könnten eifrige reine Funktionen, die mit Lazy bewertet werden, möglicherweise zur Laufzeit einen bisher nicht sichtbaren Indeterminismus für Speicherplatz und Latenz einführen.
Nichtbeendigung
Zur Kompilierungszeit kann aufgrund des Halteproblems und der gegenseitigen Rekursion in einer vollständigen Turing-Sprache nicht garantiert werden, dass Funktionen im Allgemeinen beendet werden.
Eifrig
Mit eifrig, aber nicht faul, für die Konjunktion von
Head
"und"Tail
, wenn entwederHead
oderTail
nicht endet, dann jeweils entwederList( Head(), Tail() ).tail == Tail()
oderList( Head(), Tail() ).head == Head()
nicht wahr, weil die linke Seite nicht und die rechte Seite endet.Während mit Faulheit beide Seiten enden. Daher ist eifrig mit konjunktiven Produkten zu eifrig und endet nicht (einschließlich Laufzeitausnahmen) in den Fällen, in denen dies nicht erforderlich ist.
Faul
Mit faul, aber nicht eifrig, für die Disjunktion von
1
"oder"2
, wennf
nicht endet, dannList( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tail
ist nicht wahr, weil die linke Seite endet und die rechte Seite nicht.Während mit Eifer keine Seite endet, wird der Gleichheitstest nie erreicht. Daher ist faul mit faulen Nebenprodukten zu faul und kann in diesen Fällen nicht beendet werden (einschließlich Laufzeitausnahmen), nachdem mehr Arbeit geleistet wurde, als es eifrig gewesen wäre.
[ 10 ] Deklarative Fortsetzungen und kategoriale Dualität, Filinski, Abschnitte 2.5.4 Ein Vergleich von CBV und CBN sowie 3.6.1 CBV und CBN in der SCL.
[ 11 ] Deklarative Fortsetzungen und kategoriale Dualität, Filinski, Abschnitte 2.2.1 Produkte und Nebenprodukte, 2.2.2 End- und Ausgangsobjekte, 2.5.2 CBV mit faulen Produkten und 2.5.3 CBN mit eifrigen Nebenprodukten.
quelle
Es gibt keine wirklich mehrdeutige, objektive Definition für diese. So würde ich sie definieren:
Imperativ - Der Fokus liegt darauf, welche Schritte der Computer ausführen sollte und nicht darauf, was der Computer tun wird (z. B. C, C ++, Java).
Deklarativ - Der Fokus liegt darauf, was der Computer tun soll und nicht wie er es tun soll (z. B. SQL).
Funktional - eine Teilmenge deklarativer Sprachen, bei der die Rekursion im Vordergrund steht
quelle
Imperativ und Deklarativ beschreiben zwei gegensätzliche Programmierstile. Imperativ ist der traditionelle "Schritt-für-Schritt-Rezept" -Ansatz, während deklarativ eher "das ist, was ich will, jetzt arbeiten Sie aus, wie es geht".
Diese beiden Ansätze treten während der gesamten Programmierung auf - auch mit derselben Sprache und demselben Programm. Im Allgemeinen wird der deklarative Ansatz als vorzuziehen angesehen, da er den Programmierer davon befreit, so viele Details angeben zu müssen, und gleichzeitig weniger Fehler auftreten kann (wenn Sie das gewünschte Ergebnis beschreiben und ein gut getesteter automatischer Prozess von dort bis rückwärts funktionieren kann Definieren Sie die Schritte, dann hoffen Sie vielleicht, dass die Dinge zuverlässiger sind, als jeden Schritt von Hand angeben zu müssen.
Auf der anderen Seite bietet ein zwingender Ansatz eine bessere Kontrolle auf niedriger Ebene - es ist der "Mikromanager-Ansatz" für die Programmierung. Dies kann es dem Programmierer ermöglichen, das Wissen über das Problem zu nutzen, um eine effizientere Antwort zu geben. Daher ist es nicht ungewöhnlich, dass einige Teile eines Programms in einem deklarativeren Stil geschrieben werden, aber dass die geschwindigkeitskritischen Teile zwingender sind.
Wie Sie sich vorstellen können, wirkt sich die Sprache, in der Sie ein Programm schreiben, darauf aus, wie deklarativ Sie sein können. Eine Sprache, in die "Smarts" integriert sind, um herauszufinden, was bei einer Beschreibung des Ergebnisses zu tun ist, ermöglicht eine viel deklarativere Sprache Ansatz als einer, bei dem der Programmierer zuerst diese Art von Intelligenz mit zwingendem Code hinzufügen muss, bevor er eine deklarativere Ebene darauf aufbauen kann. So wird beispielsweise eine Sprache wie Prolog als sehr deklarativ angesehen, da sie über einen integrierten Prozess verfügt, der nach Antworten sucht.
Bisher werden Sie feststellen, dass ich die funktionale Programmierung nicht erwähnt habe . Das liegt daran, dass es sich um einen Begriff handelt, dessen Bedeutung nicht unmittelbar mit den beiden anderen zusammenhängt. Im einfachsten Fall bedeutet funktionale Programmierung, dass Sie Funktionen verwenden. Insbesondere, dass Sie eine Sprache verwenden, die Funktionen als "erstklassige Werte" unterstützt - das bedeutet, dass Sie nicht nur Funktionen schreiben können, sondern auch Funktionen schreiben können, die Funktionen schreiben (die Funktionen schreiben, die ...), und Funktionen an übergeben Funktionen. Kurz gesagt - diese Funktionen sind so flexibel und allgemein wie Dinge wie Zeichenfolgen und Zahlen.
Es mag daher seltsam erscheinen, dass funktional, imperativ und deklarativ oft zusammen erwähnt werden. Der Grund dafür ist eine Folge der Idee der funktionalen Programmierung "auf das Äußerste". Eine Funktion ist im reinsten Sinne etwas aus der Mathematik - eine Art "Black Box", die Eingaben benötigt und immer die gleiche Ausgabe liefert. Für diese Art von Verhalten müssen sich ändernde Variablen nicht gespeichert werden. Wenn Sie also eine Programmiersprache entwerfen, deren Ziel es ist, eine sehr reine, mathematisch beeinflusste Idee der funktionalen Programmierung zu implementieren, lehnen Sie die Idee von Werten, die sich ändern können (in einem bestimmten, begrenzten technischen Sinne), weitgehend ab.
und wenn Sie dies tun - wenn Sie einschränken, wie sich Variablen ändern können -, zwingen Sie den Programmierer fast zufällig dazu, deklarativere Programme zu schreiben, da ein großer Teil der imperativen Programmierung beschreibt, wie sich Variablen ändern, und Sie dies nicht mehr können TU das! Es stellt sich also heraus, dass funktionale Programmierung - insbesondere Programmieren in einer funktionalen Sprache - tendenziell mehr deklarativen Code liefert.
um es zusammenzufassen:
Imperativ und Deklarativ sind zwei gegensätzliche Programmierstile (die gleichen Namen werden für Programmiersprachen verwendet, die diese Stile fördern).
Funktionale Programmierung ist ein Programmierstil, bei dem Funktionen sehr wichtig werden und infolgedessen sich ändernde Werte weniger wichtig werden. Die eingeschränkte Möglichkeit, Wertänderungen anzugeben, erzwingt einen deklarativeren Stil.
"funktionale Programmierung" wird daher oft als "deklarativ" bezeichnet.
quelle
In einer Nussschale:
Eine imperative Sprache gibt eine Reihe von Anweisungen an, die der Computer nacheinander ausführt (dies tun, dann das tun).
Eine deklarative Sprache deklariert eine Reihe von Regeln darüber, welche Ausgaben aus welchen Eingaben resultieren sollen (z. B. wenn Sie A haben, ist das Ergebnis B). Eine Engine wendet diese Regeln auf Eingaben an und gibt eine Ausgabe aus.
Eine funktionale Sprache deklariert eine Reihe von mathematisch / logischen Funktionen, die definieren, wie Eingabe in Ausgabe übersetzt wird. z.B. f (y) = y * y. Es ist eine Art deklarative Sprache.
quelle
Imperativ: Wie erreichen wir unser Ziel ?
Deklarativ: Was wir erreichen wollen
quelle
Imperative Programmierung bezeichnet jede Art der Programmierung, bei der Ihr Programm aus Anweisungen besteht, die beschreiben, wie die von einem Computer ausgeführten Vorgänge ausgeführt werden .
Deklarative Programmierung bezeichnet jeden Programmierstil, bei dem Ihr Programm entweder eine Beschreibung des Problems oder der Lösung darstellt, jedoch nicht explizit angibt, wie die Arbeit ausgeführt wird .
Funktionale Programmierung ist Programmierung durch Bewertung von Funktionen und Funktionen von Funktionen ... Als (streng definierte) funktionale Programmierung bedeutet Programmierung durch Definition von nebenwirkungsfreien mathematischen Funktionen, so dass es sich um eine Form der deklarativen Programmierung handelt, aber nicht die einzige Art der deklarativen Programmierung .
Die Logikprogrammierung (zum Beispiel in Prolog) ist eine andere Form der deklarativen Programmierung. Dabei wird berechnet, ob eine logische Aussage wahr ist (oder ob sie erfüllt werden kann). Das Programm besteht normalerweise aus einer Reihe von Fakten und Regeln - dh eher einer Beschreibung als einer Reihe von Anweisungen.
Term Rewriting (zum Beispiel CASL) ist eine andere Form der deklarativen Programmierung. Es beinhaltet die symbolische Transformation algebraischer Begriffe. Es unterscheidet sich vollständig von der Logikprogrammierung und der Funktionsprogrammierung.
quelle
Imperativ - Ausdrücke beschreiben die Abfolge der auszuführenden Aktionen (assoziativ)
deklarativ - Ausdrücke sind Deklarationen, die zum Verhalten des Programms beitragen (assoziativ, kommutativ, idempotent, monoton).
Funktionsausdrücke haben Wert als einzige Wirkung; Die Semantik unterstützt das Gleichungsdenken
quelle
Seit ich meine vorherige Antwort geschrieben habe, habe ich eine neue Definition der deklarativen Eigenschaft formuliert, die unten zitiert wird. Ich habe auch imperative Programmierung als die duale Eigenschaft definiert.
Diese Definition ist der in meiner vorherigen Antwort angegebenen überlegen, da sie prägnant und allgemeiner ist. Es kann jedoch schwieriger sein, etwas zu verstehen, da die Implikation der Unvollständigkeitssätze, die für die Programmierung und das Leben im Allgemeinen gelten, für den Menschen schwierig ist, sich Gedanken zu machen.
Die zitierte Erklärung der Definition diskutiert die Rolle der reinen funktionalen Programmierung bei der deklarativen Programmierung.
Alle exotischen Programmiertypen passen in die folgende Taxonomie von deklarativ gegenüber imperativ, da die folgende Definition behauptet, sie seien Duale.
Bearbeiten: Ich habe den folgenden Kommentar in Robert Harpers Blog gepostet :
quelle
Imperative Programmierung: Sagen Sie der „Maschine“, wie etwas zu tun ist, und als Ergebnis wird passieren, was passieren soll.
Deklarative Programmierung: Sagen Sie der „Maschine“, was passieren soll, und lassen Sie den Computer herausfinden, wie es geht.
Beispiel eines Imperativs
Beispiel für deklarativ
Hinweis: Der Unterschied besteht nicht in der Kürze, Komplexität oder Abstraktion. Wie gesagt, der Unterschied ist wie gegen was .
quelle
Heutzutage neuer Fokus: Wir brauchen die alten Klassifikationen?
Die imperativen / deklarativen / funktionalen Aspekte waren in der Vergangenheit gut, um generische Sprachen zu klassifizieren, aber heutzutage haben alle "großen Sprachen" (wie Java, Python, Javascript usw.) eine Option (normalerweise Frameworks ), um sie mit "anderem Fokus" auszudrücken. als sein Haupt (üblicher Imperativ), und parallele Prozesse, deklarative Funktionen, Lambdas usw. auszudrücken.
Eine gute Variante dieser Frage lautet also: "Welcher Aspekt ist heute gut, um Frameworks zu klassifizieren?" ... Ein wichtiger Aspekt ist etwas, das wir als "Programmierstil" bezeichnen können ...
Konzentrieren Sie sich auf die Fusion von Daten mit Algorithmus
Ein gutes Beispiel zur Erklärung. Wie Sie über jQuery bei Wikipedia lesen können ,
So jQuery ist das beste (populär) Beispiel auf einen der Fokussierung „neuen Programmierstil“ , das Objekt nicht nur Orientierung ist, sind „ Fusing Algorithmen und Datenstrukturen “. jQuery ist etwas reaktiv wie Tabellen, aber nicht „Zelle orientiert“, ist „ DOM-Knoten orientiert “ ... die wichtigsten Vergleicht man Stile in diesem Zusammenhang:
Keine Verschmelzung : In allen "großen Sprachen", in jedem funktionalen / deklarativen / imperativen Ausdruck, ist das Übliche "keine Verschmelzung" von Daten und Algorithmen, außer durch eine gewisse Objektorientierung, dh eine Verschmelzung unter strengen Gesichtspunkten der algebrischen Struktur .
Einige Fusionen : Alle klassischen Fusionsstrategien haben heutzutage einen Rahmen, der sie als Paradigma verwendet ... Datenfluss , ereignisgesteuerte Programmierung (oder alte domänenspezifische Sprachen wie awk und XSLT ) ... Wie die Programmierung mit modernen Tabellenkalkulationen auch Beispiele für reaktiven Programmierstil .
Big Fusion : „der jQuery - Stil“ ... jQuery ist eine domänenspezifische Sprache mit Schwerpunkt auf „ Verschmelzen Algorithmen und DOM-Daten-Strukturen “.
PS: Andere "Abfragesprachen" wie XQuery, SQL (mit PL als Option für zwingende Ausdrücke) sind ebenfalls Beispiele für die Fusion von Datenalgorithmen , aber es handelt sich um Inseln ohne Fusion mit anderen Systemmodulen ... Spring , wenn
find()
-varianten verwendet werden und Spezifikationsklauseln ist ein weiteres gutes Fusionsbeispiel.quelle
Deklarative Programmierung ist Programmierung, indem eine zeitlose Logik zwischen dem Eingang und dem Ausgang ausgedrückt wird, beispielsweise im Pseudocode. Das folgende Beispiel wäre deklarativ:
Wir definieren hier nur eine Beziehung, die als "Fakultät" bezeichnet wird, und definieren die Beziehung zwischen der Ausgabe und der Eingabe als diese Beziehung. Wie hier offensichtlich sein sollte, erlaubt jede strukturierte Sprache in gewissem Umfang eine deklarative Programmierung. Eine zentrale Idee der deklarativen Programmierung sind unveränderliche Daten. Wenn Sie einer Variablen zuweisen, tun Sie dies nur einmal und dann nie wieder. Andere, strengere Definitionen haben zur Folge, dass möglicherweise überhaupt keine Nebenwirkungen auftreten. Diese Sprachen werden manchmal als „rein deklarativ“ bezeichnet.
Das gleiche Ergebnis in einem imperativen Stil wäre:
In diesem Beispiel haben wir keine zeitlose statische logische Beziehung zwischen der Eingabe und der Ausgabe ausgedrückt. Wir haben die Speicheradressen manuell geändert, bis eine von ihnen das gewünschte Ergebnis enthielt. Es sollte offensichtlich sein, dass alle Sprachen eine deklarative Semantik in gewissem Umfang zulassen, aber nicht alle eine imperative, einige "rein" deklarative Sprachen erlauben Nebenwirkungen und Mutationen insgesamt.
In deklarativen Sprachen wird oft gesagt, dass sie angeben, was zu tun ist, im Gegensatz zu "wie es zu tun ist". Ich denke, das ist eine Fehlbezeichnung. Deklarative Programme geben immer noch an, wie man von der Eingabe zur Ausgabe gelangen muss, aber auf andere Weise die Die von Ihnen angegebene Beziehung muss effektiv berechenbar sein (wichtiger Begriff, suchen Sie nach, wenn Sie ihn nicht kennen). Ein anderer Ansatz ist die nichtdeterministische Programmierung, die wirklich nur angibt, welche Bedingungen ein Ergebnis erfüllt, bevor Ihre Implementierung alle Pfade für Versuch und Irrtum erschöpft, bis sie erfolgreich ist.
Zu den rein deklarativen Sprachen gehören Haskell und Pure Prolog. Eine gleitende Skala von einem zum anderen wäre: Reiner Prolog, Haskell, OCaml, Schema / Lisp, Python, Javascript, C--, Perl, PHP, C ++, Pascall, C, Fortran, Assembly
quelle
factorial
keinen Wert mutiert.Einige gute Antworten hier zu den angegebenen "Typen".
Ich reiche einige zusätzliche, "exotischere" Konzepte ein, die häufig mit der Menge der funktionalen Programmierer verbunden sind:
quelle
Ich denke, dass Ihre Taxonomie falsch ist. Es gibt zwei entgegengesetzte Typen: Imperativ und Deklarativ. Funktional ist nur ein Untertyp von deklarativ. Übrigens gibt Wikipedia die gleiche Tatsache an.
quelle
Kurz gesagt, je mehr ein Programmierstil betont, was (zu tun) ist, um die Details von Wie (zu tun) zu abstrahieren, desto mehr wird dieser Stil als deklarativ angesehen. Das Gegenteil gilt für den Imperativ. Die funktionale Programmierung ist dem deklarativen Stil zugeordnet.
quelle