Ich habe die Wikipedia-Artikel sowohl zur prozeduralen als auch zur funktionalen Programmierung gelesen , bin aber immer noch etwas verwirrt. Könnte es jemand auf den Punkt bringen?
functional-programming
glossary
paradigms
procedural-programming
Thomas Owens
quelle
quelle
Antworten:
Mit einer funktionalen Sprache können Sie (idealerweise) eine mathematische Funktion schreiben, dh eine Funktion, die n Argumente akzeptiert und einen Wert zurückgibt. Wenn das Programm ausgeführt wird, wird diese Funktion nach Bedarf logisch ausgewertet. 1
Eine prozedurale Sprache führt andererseits eine Reihe von aufeinanderfolgenden Schritten aus. (Es gibt eine Möglichkeit, sequentielle Logik in funktionale Logik umzuwandeln, die als Continuation-Passing-Stil bezeichnet wird .)
Infolgedessen liefert ein rein funktionales Programm immer den gleichen Wert für eine Eingabe, und die Reihenfolge der Auswertung ist nicht genau definiert. Dies bedeutet, dass unsichere Werte wie Benutzereingaben oder Zufallswerte in rein funktionalen Sprachen schwer zu modellieren sind.
1 Wie alles andere in dieser Antwort ist das eine Verallgemeinerung. Diese Eigenschaft, bei der eine Berechnung ausgewertet wird, wenn ihr Ergebnis benötigt wird, und nicht nacheinander, wo sie aufgerufen wird, wird als "Faulheit" bezeichnet. Nicht alle funktionalen Sprachen sind tatsächlich allgemein faul, noch ist die Faulheit auf funktionale Programmierung beschränkt. Die hier gegebene Beschreibung bietet vielmehr einen „mentalen Rahmen“, um über verschiedene Programmierstile nachzudenken, die keine unterschiedlichen und entgegengesetzten Kategorien sind, sondern fließende Ideen.
quelle
Grundsätzlich sind die beiden Stile wie Yin und Yang. Einer ist organisiert, der andere chaotisch. Es gibt Situationen, in denen funktionale Programmierung die offensichtliche Wahl ist, und andere Situationen, in denen prozedurale Programmierung die bessere Wahl ist. Aus diesem Grund gibt es mindestens zwei Sprachen, die kürzlich eine neue Version herausgebracht haben, die beide Programmierstile umfasst. ( Perl 6 und D 2 )
Verfahren:
Perl 6
D 2
Funktionell:
Haskell
(kopiert aus Wikipedia );
oder in einer Zeile:
Perl 6
D 2
Randnotiz:
Factorial ist ein gängiges Beispiel, um zu zeigen, wie einfach es ist, neue Operatoren in Perl 6 so zu erstellen, wie Sie eine Unterroutine erstellen würden. Diese Funktion ist in Perl 6 so tief verwurzelt, dass die meisten Operatoren in der Rakudo-Implementierung auf diese Weise definiert werden. Außerdem können Sie vorhandenen Operatoren Ihre eigenen Multi-Kandidaten hinzufügen.
Dieses Beispiel zeigt auch die Bereichserstellung (
2..$n
) und den Meta-Operator zur Listenreduzierung ([ OPERATOR ] LIST
) in Kombination mit dem Multiplikationsoperator für numerische Infixe. (*
)Es zeigt auch, dass Sie
--> UInt
die Signatur anstattreturns UInt
danach eingeben können.(Sie können mit dem Starten des Bereichs davonkommen,
2
da der Multiplikations- "Operator"1
beim Aufruf ohne Argumente zurückkehrt.)quelle
sub postfix:<!> ($n) { [*] 1..$n }
No operation can have side effects
Kannst du es bitte näher erläutern?sub foo( $a, $b ){ ($a,$b).pick }
← gibt nicht immer die gleiche Ausgabe für die gleiche Eingabe zurück, während die folgendensub foo( $a, $b ){ $a + $b }
Ich habe diese Definition an keiner anderen Stelle gesehen, aber ich denke, dies fasst die hier angegebenen Unterschiede ziemlich gut zusammen:
Die funktionale Programmierung konzentriert sich auf Ausdrücke
Die prozedurale Programmierung konzentriert sich auf Anweisungen
Ausdrücke haben Werte. Ein Funktionsprogramm ist ein Ausdruck, dessen Wert eine Folge von Anweisungen ist, die der Computer ausführen muss.
Anweisungen haben keine Werte und ändern stattdessen den Status einer konzeptionellen Maschine.
In einer rein funktionalen Sprache würde es keine Anweisungen geben, in dem Sinne, dass es keine Möglichkeit gibt, den Status zu manipulieren (sie könnten immer noch ein syntaktisches Konstrukt namens "Anweisung" haben, aber wenn es den Status nicht manipuliert, würde ich es nicht als Anweisung in diesem Sinne bezeichnen ). In einer rein prozeduralen Sprache würde es keine Ausdrücke geben, alles wäre eine Anweisung, die den Zustand der Maschine manipuliert.
Haskell wäre ein Beispiel für eine rein funktionale Sprache, da es keine Möglichkeit gibt, den Zustand zu manipulieren. Maschinencode wäre ein Beispiel für eine rein prozedurale Sprache, da alles in einem Programm eine Anweisung ist, die den Status der Register und den Speicher der Maschine manipuliert.
Der verwirrende Teil ist, dass die überwiegende Mehrheit der Programmiersprachen sowohl Ausdrücke als auch Anweisungen enthält, sodass Sie Paradigmen mischen können. Sprachen können als funktionaler oder prozeduraler eingestuft werden, je nachdem, wie sehr sie die Verwendung von Anweisungen gegenüber Ausdrücken fördern.
Zum Beispiel wäre C funktionaler als COBOL, da ein Funktionsaufruf ein Ausdruck ist, während das Aufrufen eines Unterprogramms in COBOL eine Anweisung ist (die den Status gemeinsam genutzter Variablen manipuliert und keinen Wert zurückgibt). Python wäre funktionaler als C, da Sie damit bedingte Logik als Ausdruck mithilfe der Kurzschlussauswertung ausdrücken können (test && path1 || path2 im Gegensatz zu if-Anweisungen). Das Schema wäre funktionaler als Python, da alles im Schema ein Ausdruck ist.
Sie können immer noch in einem funktionalen Stil in einer Sprache schreiben, die das prozedurale Paradigma fördert und umgekehrt. Es ist nur schwieriger und / oder umständlicher, in einem Paradigma zu schreiben, das von der Sprache nicht gefördert wird.
quelle
In der Informatik ist die funktionale Programmierung ein Programmierparadigma, das die Berechnung als Bewertung mathematischer Funktionen behandelt und Zustands- und veränderbare Daten vermeidet. Es betont die Anwendung von Funktionen im Gegensatz zu dem prozeduralen Programmierstil, der Zustandsänderungen betont.
quelle
GetUserContext()
die Funktion eingeben, der Benutzerkontext würde übergeben. Ist das funktionale Programmierung? Danke im Voraus.Ich glaube, dass es bei der prozeduralen / funktionalen / objektiven Programmierung darum geht, wie man ein Problem angeht.
Der erste Stil würde alles in Schritten planen und das Problem lösen, indem jeweils ein Schritt (eine Prozedur) implementiert wird. Andererseits würde die funktionale Programmierung den Divide-and-Conquer-Ansatz hervorheben, bei dem das Problem in ein Unterproblem unterteilt wird, dann jedes Unterproblem gelöst wird (wodurch eine Funktion zur Lösung dieses Unterproblems erstellt wird) und die Ergebnisse kombiniert werden Erstellen Sie die Antwort für das gesamte Problem. Schließlich würde die objektive Programmierung die reale Welt nachahmen, indem eine Mini-Welt im Computer mit vielen Objekten erstellt wird, von denen jedes (etwas) einzigartige Eigenschaften aufweist und mit anderen interagiert. Aus diesen Wechselwirkungen würde das Ergebnis hervorgehen.
Jeder Programmierstil hat seine eigenen Vor- und Nachteile. Daher ist es sehr schwierig, wenn nicht unmöglich, etwas wie "reine Programmierung" zu tun (dh rein prozedural - das tut übrigens niemand, was irgendwie komisch ist - oder rein funktional oder rein objektiv), mit Ausnahme einiger elementarer Probleme entwickelt, um den Vorteil eines Programmierstils zu demonstrieren (daher nennen wir diejenigen, die Reinheit mögen, "weenie": D).
Aus diesen Stilen haben wir dann Programmiersprachen, die für jeden Stil optimiert sind. Bei der Versammlung dreht sich beispielsweise alles um Verfahren. Okay, die meisten frühen Sprachen sind prozedural, nicht nur Asm, wie C, Pascal (und Fortran, wie ich gehört habe). Dann haben wir alle berühmtes Java in der objektiven Schule (Java und C # gehören eigentlich auch zu einer Klasse namens "geldorientiert", aber das ist Gegenstand einer weiteren Diskussion). Ebenfalls objektiv ist Smalltalk. In der funktionalen Schule hätten wir "fast funktionale" (einige hielten sie für unrein) Lisp-Familie und ML-Familie und viele "rein funktionale" Haskell, Erlang usw. Übrigens gibt es viele allgemeine Sprachen wie Perl, Python , Ruby.
quelle
Funktionsprogrammierung
Verfahrensprogrammierung
function_to_add_one
ist eine Funktionprocedure_to_add_one
ist eine ProzedurSelbst wenn Sie die Funktion fünfmal ausführen , wird jedes Mal 2 zurückgegeben
Wenn Sie die Prozedur fünfmal ausführen , erhalten Sie am Ende des fünften Durchlaufs 6 .
quelle
Um Konrads Kommentar zu erweitern:
Aus diesem Grund ist Funktionscode im Allgemeinen einfacher zu parallelisieren. Da es (im Allgemeinen) keine Nebenwirkungen der Funktionen gibt und sie (im Allgemeinen) nur auf ihre Argumente reagieren, verschwinden viele Parallelitätsprobleme.
Funktionale Programmierung wird auch verwendet, wenn Sie in der Lage sein müssen, zu beweisen Ihr Code korrekt ist. Dies ist bei der prozeduralen Programmierung viel schwieriger (nicht einfach mit funktionalen, aber immer noch einfacher).
Haftungsausschluss: Ich habe seit Jahren keine funktionale Programmierung mehr verwendet und erst vor kurzem wieder angefangen, sie zu betrachten, sodass ich hier möglicherweise nicht ganz richtig bin. :) :)
quelle
Eine Sache, die ich hier nicht wirklich betont hatte, ist, dass moderne Funktionssprachen wie Haskell mehr auf erstklassigen Funktionen zur Flusskontrolle als auf expliziter Rekursion basieren. Sie müssen in Haskell keine faktorielle rekursive Definition vornehmen, wie oben beschrieben. Ich denke so etwas
ist eine vollkommen idiomatische Konstruktion und im Geiste der Verwendung einer Schleife viel näher als der Verwendung einer expliziten Rekursion.
quelle
Eine funktionale Programmierung ist identisch mit einer prozeduralen Programmierung, bei der keine globalen Variablen verwendet werden.
quelle
Prozedurale Sprachen neigen dazu, den Status zu verfolgen (unter Verwendung von Variablen) und werden in der Regel als Folge von Schritten ausgeführt. Rein funktionale Sprachen verfolgen den Status nicht, verwenden unveränderliche Werte und werden in der Regel als eine Reihe von Abhängigkeiten ausgeführt. In vielen Fällen enthält der Status des Aufrufstapels die Informationen, die denen entsprechen, die in Statusvariablen im Prozedurcode gespeichert sind.
Rekursion ist ein klassisches Beispiel für funktionale Programmierung.
quelle
Konrad sagte:
Die Reihenfolge der Auswertung in einem rein funktionalen Programm mag schwer (ähm) zu begründen sein (insbesondere mit Faulheit) oder sogar unwichtig, aber ich denke, wenn man sagt, dass es nicht genau definiert ist, hört es sich so an, als könne man nicht sagen, ob Ihr Programm läuft überhaupt arbeiten!
Eine bessere Erklärung wäre vielleicht, dass der Kontrollfluss in Funktionsprogrammen davon abhängt, wann der Wert der Argumente einer Funktion benötigt wird. Das Gute daran ist, dass in gut geschriebenen Programmen der Status explizit wird: Jede Funktion listet ihre Eingaben als Parameter auf, anstatt den globalen Status willkürlich zu ändern . Auf einer bestimmten Ebene ist es daher einfacher, die Reihenfolge der Bewertung in Bezug auf jeweils eine Funktion zu bestimmen . Jede Funktion kann den Rest des Universums ignorieren und sich auf das konzentrieren, was sie tun muss. In Kombination funktionieren Funktionen garantiert genauso [1] wie isoliert.
Die Lösung für das Eingabeproblem in rein funktionalen Programmen besteht darin, eine imperative Sprache als DSL unter Verwendung einer ausreichend leistungsfähigen Abstraktion einzubetten . In imperativen (oder nicht reinen funktionalen) Sprachen ist dies nicht erforderlich, da Sie den Status implizit "betrügen" und übergeben können und die Reihenfolge der Bewertung explizit ist (ob Sie es mögen oder nicht). Aufgrund dieses "Betrugs" und der erzwungenen Auswertung aller Parameter für jede Funktion verlieren Sie in wichtigen Sprachen 1) die Fähigkeit, eigene Kontrollflussmechanismen (ohne Makros) zu erstellen, 2) Code ist nicht von Natur aus threadsicher und / oder parallelisierbar standardmäßig, 3) und das Implementieren von etwas wie Rückgängig (Zeitreise) erfordert sorgfältige Arbeit (ein zwingender Programmierer muss ein Rezept speichern, um die alten Werte zurückzubekommen!), Während reine funktionale Programmierung Ihnen all diese Dinge kauft - und ein paar mehr kann ich vergessen haben - "kostenlos".
Ich hoffe, das klingt nicht nach Eifer, ich wollte nur eine Perspektive hinzufügen. Imperative Programmierung und insbesondere gemischte Paradigmenprogrammierung in leistungsstarken Sprachen wie C # 3.0 sind immer noch absolut effektive Methoden, um Dinge zu erledigen, und es gibt keine Silberkugel .
[1] ... außer möglicherweise in Bezug auf die Speichernutzung (vgl. Foldl und foldl 'in Haskell).
quelle
Um Konrads Kommentar zu erweitern:
Einige funktionale Sprachen haben eine sogenannte Lazy Evaluation. Dies bedeutet, dass eine Funktion erst ausgeführt wird, wenn der Wert benötigt wird. Bis zu diesem Zeitpunkt wird die Funktion selbst weitergegeben.
Prozedurale Sprachen sind Schritt 1, Schritt 2, Schritt 3 ... Wenn Sie in Schritt 2 sagen, dass Sie 2 + 2 hinzufügen, macht es das richtig. In der verzögerten Auswertung würde man sagen, addiere 2 + 2, aber wenn das Ergebnis nie verwendet wird, wird es niemals addiert.
quelle
Wenn Sie eine Chance haben, würde ich empfehlen, eine Kopie von Lisp / Scheme zu erhalten und einige Projekte darin durchzuführen. Die meisten Ideen, die in letzter Zeit zu Bandwaggons geworden sind, wurden vor Jahrzehnten in Lisp zum Ausdruck gebracht: funktionale Programmierung, Fortsetzungen (als Abschlüsse), Speicherbereinigung, sogar XML.
Das wäre also ein guter Weg, um sich einen Vorsprung bei all diesen aktuellen Ideen zu verschaffen, und noch ein paar mehr, wie zum Beispiel die symbolische Berechnung.
Sie sollten wissen, wofür funktionale Programmierung gut ist und wofür sie nicht gut ist. Es ist nicht für alles gut. Einige Probleme lassen sich am besten in Form von Nebenwirkungen ausdrücken, bei denen dieselbe Frage je nach Zeitpunkt der Beantwortung unterschiedliche Antworten gibt.
quelle
@Creighton:
In Haskell gibt es eine Bibliotheksfunktion namens product :
oder einfach:
also die "idiomatische" Fakultät
wäre einfach
quelle
Die prozedurale Programmierung unterteilt Sequenzen von Anweisungen und bedingten Konstrukten in separate Blöcke, die als Prozeduren bezeichnet werden und über Argumente parametrisiert werden, die (nicht funktionale) Werte sind.
Die funktionale Programmierung ist dieselbe, außer dass Funktionen erstklassige Werte sind, sodass sie als Argumente an andere Funktionen übergeben und als Ergebnis von Funktionsaufrufen zurückgegeben werden können.
Beachten Sie, dass die funktionale Programmierung eine Verallgemeinerung der prozeduralen Programmierung in dieser Interpretation ist. Eine Minderheit interpretiert "funktionale Programmierung" jedoch als nebenwirkungsfrei, was sehr unterschiedlich, aber für alle wichtigen funktionalen Sprachen außer Haskell irrelevant ist.
quelle
Um den Unterschied zu verstehen, muss man verstehen, dass das "Paten" -Paradigma sowohl der prozeduralen als auch der funktionalen Programmierung die zwingende Programmierung ist .
Grundsätzlich ist die prozedurale Programmierung lediglich eine Möglichkeit, imperative Programme zu strukturieren, bei denen die primäre Abstraktionsmethode die "Prozedur" ist. (oder "Funktion" in einigen Programmiersprachen). Sogar die objektorientierte Programmierung ist nur eine andere Möglichkeit, ein imperatives Programm zu strukturieren, bei dem der Status in Objekten eingekapselt ist und zu einem Objekt mit einem "aktuellen Status" wird. Außerdem verfügt dieses Objekt über eine Reihe von Funktionen, Methoden und anderen Dingen, mit denen Sie das tun können Programmierer manipulieren oder aktualisieren den Status.
In Bezug auf die funktionale Programmierung besteht der Kern seines Ansatzes darin, zu identifizieren, welche Werte zu nehmen sind und wie diese Werte übertragen werden sollen. (Es gibt also keinen Status und keine veränderlichen Daten, da Funktionen als erstklassige Werte verwendet und als Parameter an andere Funktionen übergeben werden.)
PS: Das Verständnis jedes Programmierparadigmas sollte die Unterschiede zwischen allen klären.
PSS: Letztendlich sind Programmierparadigmen nur verschiedene Ansätze zur Lösung von Problemen.
PSS: Diese Quora-Antwort hat eine großartige Erklärung.
quelle
Keine der Antworten hier zeigt eine idiomatische funktionale Programmierung. Die rekursive faktorielle Antwort eignet sich hervorragend zur Darstellung der Rekursion in FP, aber der Großteil des Codes ist nicht rekursiv, sodass ich nicht denke, dass die Antwort vollständig repräsentativ ist.
Angenommen, Sie haben ein Array von Zeichenfolgen, und jede Zeichenfolge repräsentiert eine Ganzzahl wie "5" oder "-200". Sie möchten dieses Eingabe-Array von Zeichenfolgen mit Ihrem internen Testfall vergleichen (Verwenden eines Ganzzahlvergleichs). Beide Lösungen sind unten gezeigt
Verfahren
Funktionell
Während reine funktionale Sprachen im Allgemeinen Forschungssprachen sind (da die reale Welt freie Nebenwirkungen mag), verwenden reale prozedurale Sprachen bei Bedarf die viel einfachere funktionale Syntax.
Dies wird normalerweise mit einer externen Bibliothek wie Lodash implementiert oder ist in neueren Sprachen wie Rust integriert . Das Heben schwerer Lasten der funktionalen Programmierung mit Funktionen / Konzepte gemacht wie
map
,filter
,reduce
,currying
,partial
, die letzten drei , von denen Sie für das weitere Verständnis nachschlagen kann.Nachtrag
Um in freier Wildbahn verwendet zu werden, muss der Compiler normalerweise herausfinden, wie die Funktionsversion intern in die prozedurale Version konvertiert wird, da der Overhead für Funktionsaufrufe zu hoch ist. Rekursive Fälle wie die gezeigte Fakultät verwenden Tricks wie Tail Call , um die Verwendung von O (n) -Speicher zu entfernen. Die Tatsache, dass es keine Nebenwirkungen gibt, ermöglicht es funktionalen Compilern, die
&& ret
Optimierung auch dann zu implementieren , wenn.reduce
dies zuletzt durchgeführt wurde. Die Verwendung von Lodash in JS ermöglicht offensichtlich keine Optimierung, sodass die Leistung beeinträchtigt wird (was bei der Webentwicklung normalerweise kein Problem darstellt). Sprachen wie Rust werden intern optimiert (und verfügen über Funktionentry_fold
zur Unterstützung der&& ret
Optimierung).quelle