Frage

Ich habe die Wikipedia-Artikel für beide gelesen Verfahrensprogrammierung Und funktionale Programmierung, aber ich bin immer noch etwas verwirrt.Könnte es jemand auf den Punkt bringen?

War es hilfreich?

Lösung

Eine funktionale Sprache ermöglicht es Ihnen (idealerweise), eine mathematische Funktion zu schreiben, d. h.eine Funktion, die dauert N Argumente und gibt einen Wert zurück.Wird das Programm ausgeführt, wird diese Funktion bei Bedarf logisch ausgewertet.1

Eine prozedurale Sprache hingegen führt eine Reihe von aus sequentiell Schritte.(Es gibt eine Möglichkeit, sequentielle Logik in funktionale Logik umzuwandeln, genannt Fortsetzungspassstil.)

Als Konsequenz ergibt sich immer ein rein funktionales Programm den gleichen Wert für eine Eingabe und die Reihenfolge der Auswertung ist nicht genau definiert;Das 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.Die Eigenschaft, eine Berechnung dann auszuwerten, wenn ihr Ergebnis benötigt wird, und nicht nacheinander, wo sie aufgerufen wird, wird als „Faulheit“ bezeichnet. Nicht alle funktionalen Sprachen sind tatsächlich universell faul, und Faulheit ist auch nicht auf funktionale Programmierung beschränkt.Vielmehr bietet die hier gegebene Beschreibung einen „mentalen Rahmen“, um über verschiedene Programmierstile nachzudenken, bei denen es sich nicht um unterschiedliche und gegensätzliche Kategorien handelt, sondern um fließende Ideen.

Andere Tipps

Im Grunde ähneln die beiden Stile Yin und Yang.Das eine ist organisiert, das andere chaotisch.Es gibt Situationen, in denen die funktionale Programmierung die offensichtliche Wahl ist, und in anderen Situationen ist die prozedurale Programmierung die bessere Wahl.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 )

Ablauf:

  • Die Ausgabe einer Routine steht nicht immer in direktem Zusammenhang mit der Eingabe.
  • Alles wird in einer bestimmten Reihenfolge erledigt.
  • Die Ausführung einer Routine kann Nebenwirkungen haben.
  • Legt Wert darauf, Lösungen linear umzusetzen.

Perl 6

D 2

int factorial( int n ){

  int result = 1;

  for( ; n > 0 ; n-- ){
    result *= n;
  }

  return result;
}

Funktionell:

  • Oft rekursiv.
  • Gibt immer die gleiche Ausgabe für eine bestimmte Eingabe zurück.
  • Die Reihenfolge der Auswertung ist in der Regel nicht definiert.
  • Muss staatenlos sein.d.h.Keine Operation kann Nebenwirkungen haben.
  • Gute Eignung für parallele Ausführung
  • Neigt dazu, den „Teile-und-Herrsche“-Ansatz zu betonen.
  • Kann über die Funktion „Lazy Evaluation“ verfügen.

Haskell

(kopiert von Wikipedia );

fac :: Integer -> Integer

fac 0 = 1
fac n | n > 0 = n * fac (n-1)

oder in einer Zeile:

fac n = if n > 0 then n * fac (n-1) else 1

Perl 6

D 2

pure int factorial( invariant int n ){
  if( n <= 1 ){
    return 1;
  }else{
    return n * factorial( n-1 );
  }
}

Randnotiz:

Factorial ist tatsächlich ein häufiges Beispiel, um zu zeigen, wie einfach es ist, neue Operatoren in Perl 6 auf die gleiche Weise 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 sind.Außerdem können Sie Ihre eigenen Multikandidaten zu vorhandenen Operatoren hinzufügen.

Dieses Beispiel zeigt auch die Bereichserstellung (2..$n) und der Metaoperator zur Listenreduzierung ([ OPERATOR ] LIST) kombiniert mit dem numerischen Infix-Multiplikationsoperator.(*)
Es zeigt auch, dass Sie setzen können --> UInt in der Signatur statt returns UInt Danach.

(Sie können davonkommen, den Bereich mit zu beginnen 2 da der Multiplikationsoperator zurückkehrt 1 wenn ohne Argumente aufgerufen)

Ich habe diese Definition noch nie anderswo gesehen, aber ich denke, dass dies die hier angegebenen Unterschiede ziemlich gut zusammenfasst:

Funktional Die Programmierung konzentriert sich auf Ausdrücke

Verfahrenstechnisch Die Programmierung konzentriert sich auf Aussagen

Ausdrücke haben Werte.Ein Funktionsprogramm ist ein Ausdruck, dessen Wert eine Folge von Anweisungen ist, die der Computer ausführen soll.

Anweisungen haben keine Werte und ändern stattdessen den Zustand einer konzeptionellen Maschine.

In einer rein funktionalen Sprache gäbe es keine Anweisungen in dem Sinne, dass es keine Möglichkeit gibt, den Zustand zu manipulieren (sie könnten immer noch ein syntaktisches Konstrukt namens „Anweisung“ haben, aber wenn es den Zustand nicht manipuliert, würde ich es nicht als Anweisung in diesem Sinne bezeichnen). ).In einer rein prozeduralen Sprache gäbe es keine Ausdrücke, 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 Zustand der Register und des Speichers der Maschine manipuliert.

Das Verwirrende daran ist, dass die überwiegende Mehrheit der Programmiersprachen Folgendes enthält beide Ausdrücke und Aussagen, die es Ihnen ermöglichen, Paradigmen zu mischen.Sprachen können je nachdem, wie sehr sie die Verwendung von Anweisungen statt Ausdrücken fördern, als eher funktional oder eher prozedural klassifiziert werden.

Beispielsweise wäre C funktionaler als COBOL, da ein Funktionsaufruf ein Ausdruck ist, wohingegen der Aufruf eines Unterprogramms in COBOL eine Anweisung ist (die den Zustand gemeinsamer Variablen manipuliert und keinen Wert zurückgibt).Python wäre funktionaler als C, da es Ihnen ermöglicht, bedingte Logik als Ausdruck mithilfe einer Kurzschlussauswertung auszudrücken (Test && Pfad1 || Pfad2 im Gegensatz zu if-Anweisungen).Scheme wäre funktionaler als Python, da alles in scheme 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 einfach schwieriger und/oder umständlicher, in einem Paradigma zu schreiben, das von der Sprache nicht gefördert wird.

In der Informatik ist funktionale Programmierung ein Programmierparadigma, das Berechnungen als Auswertung mathematischer Funktionen behandelt und Zustands- und veränderliche Daten vermeidet.Der Schwerpunkt liegt auf der Anwendung von Funktionen, im Gegensatz zum prozeduralen Programmierstil, bei dem Zustandsänderungen im Vordergrund stehen.

Ich glaube, dass es bei der prozeduralen/funktionalen/objektiven Programmierung darum geht, wie man ein Problem angeht.

Der erste Stil plant alles in Schritten und löst das Problem durch die Implementierung eines Schritts (einer Prozedur) nach dem anderen.Andererseits würde die funktionale Programmierung den „Teile-und-Herrsche“-Ansatz betonen, bei dem das Problem in Unterprobleme 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 sie eine Miniwelt im Computer mit vielen Objekten schafft, von denen jedes (etwas) einzigartige Eigenschaften hat und mit anderen interagiert.Aus diesen Interaktionen würde das Ergebnis hervorgehen.

Jeder Programmierstil hat seine eigenen Vorteile und Schwächen.Wenn man also etwas wie „reines Programmieren“ macht (d. h.rein prozedural – das macht übrigens niemand, was irgendwie seltsam ist – oder rein funktional oder rein objektiv) ist sehr schwierig, wenn nicht unmöglich, mit Ausnahme einiger elementarer Probleme, die speziell dafür entwickelt wurden, die Vorteile eines Programmierstils zu demonstrieren (daher wir nennen 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 das 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 das berühmte Java in der objektiven Schule (Eigentlich gehören Java und C# auch zu einer Klasse namens „geldorientiert“, aber das ist Gegenstand einer anderen Diskussion).Objektiv ist auch Smalltalk.In der funktionalen Schule hätten wir „nahezu funktionale“ (manche hielten sie für unreine) Lisp-Familie und ML-Familie und viele „rein funktionale“ Haskell, Erlang usw.Übrigens gibt es viele allgemeine Sprachen wie Perl, Python, Ruby.

Um Konrads Kommentar zu erweitern:

Dies hat zur Folge, dass ein rein funktionales Programm immer den gleichen Wert für eine Eingabe liefert und die Reihenfolge der Auswertung nicht genau definiert ist;

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 Probleme mit der Parallelität.

Funktionale Programmierung wird auch verwendet, wenn Sie dazu in der Lage sein müssen beweisen Dein Code ist korrekt.Dies ist mit prozeduraler Programmierung viel schwieriger zu bewerkstelligen (nicht einfach mit funktionaler Programmierung, aber immer noch einfacher).

Haftungsausschluss:Ich habe seit Jahren keine funktionale Programmierung mehr verwendet und mich erst vor kurzem wieder damit beschäftigt, daher liege ich hier möglicherweise nicht ganz richtig.:) :)

Eine Sache, die ich hier nicht wirklich hervorgehoben hatte, ist, dass moderne funktionale Sprachen wie Haskell eher auf erstklassige Funktionen zur Flusskontrolle als auf explizite Rekursion setzen.Sie müssen die Fakultät in Haskell nicht rekursiv definieren, wie oben beschrieben.Ich denke so etwas wie

fac n = foldr (*) 1 [1..n]

ist eine vollkommen idiomatische Konstruktion und im Geiste der Verwendung einer Schleife viel näher als der Verwendung einer expliziten Rekursion.

Eine funktionale Programmierung ist identisch mit einer prozeduralen Programmierung, in der es globale Variablen gibt nicht verwendet werden.

Prozedurale Sprachen neigen dazu, den Status zu verfolgen (mithilfe von Variablen) und werden tendenziell als eine Folge von Schritten ausgeführt.Rein funktionale Sprachen verfolgen den Status nicht, verwenden unveränderliche Werte und werden tendenziell als eine Reihe von Abhängigkeiten ausgeführt.In vielen Fällen enthält der Status des Aufrufstapels die Informationen, die denen entsprechen würden, die in Zustandsvariablen im prozeduralen Code gespeichert würden.

Rekursion ist ein klassisches Beispiel für die Programmierung im funktionalen Stil.

Konrad sagte:

Infolgedessen ergibt ein rein funktionelles Programm immer den gleichen Wert für eine Eingabe, und die Reihenfolge der Bewertung ist nicht genau definiert.Dies bedeutet, dass unsichere Werte wie Benutzereingaben oder zufällige Werte in rein funktionalen Sprachen schwer zu modellieren sind.

Über die Reihenfolge der Auswertung in einem rein funktionalen Programm kann man sich schwer (besonders bei Faulheit) Gedanken machen oder sie ist sogar unwichtig, aber wenn man sagt, dass sie nicht genau definiert ist, klingt es meiner Meinung nach so, als könne man nicht sagen, ob das Programm läuft überhaupt arbeiten!

Eine bessere Erklärung wäre vielleicht, dass der Kontrollfluss in funktionalen Programmen darauf basiert, wann der Wert der Argumente einer Funktion benötigt wird.Das Gute daran ist, dass in gut geschriebenen Programmen der Zustand explizit wird:Jede Funktion listet ihre Eingaben als Parameter auf und nicht willkürlich munging globaler Staat.Also auf einer gewissen Ebene, Es ist einfacher, über die Reihenfolge der Auswertung in Bezug auf jeweils eine Funktion nachzudenken.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.

...Unsichere Werte wie Benutzereingaben oder zufällige Werte sind in rein funktionalen Sprachen schwer zu modellieren.

Die Lösung des Eingabeproblems in rein funktionalen Programmen besteht darin, eine imperative Sprache als eine einzubetten DSL verwenden eine ausreichend leistungsfähige Abstraktion.In imperativen (oder nicht rein funktionalen) Sprachen ist dies nicht erforderlich, da Sie „schummeln“ und den Status implizit übergeben können und die Reihenfolge der Auswertung explizit ist (ob es Ihnen gefällt oder nicht).Aufgrund dieses „Schummelns“ und der erzwungenen Auswertung aller Parameter für jede Funktion verlieren Sie in imperativen Sprachen 1) die Möglichkeit, Ihre eigenen Kontrollflussmechanismen (ohne Makros) zu erstellen, 2) Code ist nicht von Natur aus threadsicher und/oder parallelisierbar standardmäßig, 3) und die Implementierung von so etwas wie „Rückgängig“ (Zeitreise) erfordert sorgfältige Arbeit (zwingender Programmierer muss ein Rezept speichern, um die alten Werte wiederherzustellen!), wohingegen reine funktionale Programmierung Ihnen all diese Dinge erkauft – und vielleicht noch ein paar mehr vergessen haben – „kostenlos“.

Ich hoffe, das klingt nicht nach Eifer, ich wollte nur etwas Perspektive hinzufügen.Imperative Programmierung und insbesondere die Programmierung mit gemischten Paradigmen in leistungsstarken Sprachen wie C# 3.0 sind immer noch äußerst effektive Methoden, um Dinge zu erledigen Es gibt keine Wunderwaffe.

[1] ...außer möglicherweise hinsichtlich der Speichernutzung (vgl.Foldl und Foldl' in Haskell).

Um Konrads Kommentar zu erweitern:

und die Reihenfolge der Bewertung ist nicht genau definiert

Einige funktionale Sprachen verfügen über eine sogenannte Lazy Evaluation.Das bedeutet, dass eine Funktion erst dann ausgeführt wird, wenn der Wert benötigt wird.Bis dahin wird die Funktion selbst weitergegeben.

Prozedurale Sprachen sind Schritt 1, Schritt 2, Schritt 3 ...Wenn Sie in Schritt 2 sagen, dass Sie 2 + 2 addieren sollen, funktioniert es genau dann richtig.Bei der verzögerten Auswertung würde man sagen: Addiere 2 + 2, aber wenn das Ergebnis nie verwendet wird, wird die Addition nie durchgeführt.

Wenn Sie die Möglichkeit haben, würde ich Ihnen empfehlen, sich eine Kopie von Lisp/Scheme zu besorgen und einige Projekte damit durchzuführen.Die meisten Ideen, die in letzter Zeit zum Trend geworden sind, wurden vor Jahrzehnten in Lisp zum Ausdruck gebracht:funktionale Programmierung, Fortsetzungen (als Abschlüsse), Garbage Collection, sogar XML.

Das wäre also eine gute Möglichkeit, sich einen Vorsprung bei all diesen aktuellen Ideen und einigen weiteren Ideen zu verschaffen, etwa der symbolischen Berechnung.

Sie sollten wissen, wofür funktionale Programmierung gut ist und wofür nicht.Es ist nicht für alles gut.Manche Probleme lassen sich am besten in Form von Nebenwirkungen ausdrücken, bei denen dieselbe Frage je nachdem, wann sie gestellt wird, unterschiedliche Antworten liefert.

@Creighton:

In Haskell gibt es eine Bibliotheksfunktion namens Produkt:

prouduct list = foldr 1 (*) list

oder einfach:

product = foldr 1 (*)

also die „idiomatische“ Fakultät

fac n = foldr 1 (*)  [1..n]

wäre einfach so

fac n = product [1..n]

Funktionale Programmierung

num = 1 
def function_to_add_one(num):
    num += 1
    return num


function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)
function_to_add_one(num)

#Final Output: 2

Verfahrensprogrammierung

num = 1 
def procedure_to_add_one():
    global num
    num += 1
    return num


procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()
procedure_to_add_one()

#Final Output: 6

function_to_add_one ist eine Funktion

procedure_to_add_one ist ein Verfahren

Auch wenn Sie das ausführen Funktion fünfmal, jedes Mal kommt es zurück 2

Wenn Sie das ausführen Verfahren fünf Mal, am Ende des fünften Durchgangs erhalten Sie Folgendes 6.

Die prozedurale Programmierung unterteilt Sequenzen von Anweisungen und bedingten Konstrukten in separate Blöcke, sogenannte Prozeduren, die ü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 Ergebnisse von Funktionsaufrufen zurückgegeben werden können.

Beachten Sie, dass die funktionale Programmierung in dieser Interpretation eine Verallgemeinerung der prozeduralen Programmierung ist.Allerdings interpretiert eine Minderheit „funktionale Programmierung“ als nebenwirkungsfrei, was ganz anders ist, aber für alle wichtigen funktionalen Sprachen außer Haskell irrelevant ist.

Um den Unterschied zu verstehen, muss man verstehen, dass das „Paten“-Paradigma sowohl der prozeduralen als auch der funktionalen Programmierung das ist Imperative Programmierung.

Grundsätzlich ist die prozedurale Programmierung lediglich ein Weg, um imperative Programme zu strukturieren, bei denen die Hauptmethode der Abstraktion das "Verfahren" ist. (oder "Funktion" in einigen Programmiersprachen).Sogar die objektorientierte Programmierung ist nur eine weitere Möglichkeit, ein imperatives Programm zu strukturieren, bei dem der Zustand in Objekten gekapselt wird und zu einem Objekt mit einem „aktuellen Zustand“ wird. Außerdem verfügt dieses Objekt über eine Reihe von Funktionen, Methoden und anderen Dingen, mit denen Sie dies tun können Programmierer manipulieren oder aktualisieren den Status.

Was nun die funktionale Programmierung betrifft, so ist die Kern Der Ansatz besteht darin, zu ermitteln, welche Werte angenommen werden sollen und wie diese Werte übertragen werden sollen.(Es gibt also keinen Zustand und keine veränderlichen Daten, da Funktionen als erstklassige Werte angenommen und als Parameter an andere Funktionen übergeben werden.)

PS:Das Verständnis jedes Programmierparadigmas, für das es verwendet wird, sollte die Unterschiede zwischen allen verdeutlichen.

PSS:Letztlich sind Programmierparadigmen lediglich unterschiedliche Ansätze zur Lösung von Problemen.

PSS: Das Die Antwort von Quora hat eine tolle Erklärung.

Keine der Antworten hier zeigt 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, daher glaube ich nicht, dass diese Antwort vollständig repräsentativ ist.

Angenommen, Sie haben ein Array von Zeichenfolgen und jede Zeichenfolge stellt eine Ganzzahl wie „5“ oder „-200“ dar.Sie möchten dieses Eingabearray von Zeichenfolgen mit Ihrem internen Testfall vergleichen (mittels Ganzzahlvergleich).Beide Lösungen sind unten dargestellt

Verfahrenstechnisch

arr_equal(a : [Int], b : [Str]) -> Bool {
    if(a.len != b.len) {
        return false;
    }

    bool ret = true;
    for( int i = 0; i < a.len /* Optimized with && ret*/; i++ ) {
        int a_int = a[i];
        int b_int = parseInt(b[i]);
        ret &= a_int == b_int;  
    }
    return ret;
}

Funktional

eq = i, j => i == j # This is usually a built-in
toInt = i => parseInt(i) # Of course, parseInt === toInt here, but this is for visualization

arr_equal(a : [Int], b : [Str]) -> Bool =
    zip(a, b.map(toInt)) # Combines into [Int, Int]
   .map(eq)
   .reduce(true, (i, j) => i && j) # Start with true, and continuously && it with each value

Während reine funktionale Sprachen im Allgemeinen Forschungssprachen sind (da die reale Welt kostenlose Nebenwirkungen mag), verwenden prozedurale Sprachen der realen Welt gegebenenfalls die viel einfachere funktionale Syntax.

Dies wird normalerweise mit einer externen Bibliothek wie z Lodash, oder integriert mit neueren Sprachen verfügbar wie Rost.Die schwere Arbeit der funktionalen Programmierung erfolgt mit Funktionen/Konzepten wie map, filter, reduce, currying, partial, die letzten drei davon können Sie zum weiteren Verständnis nachschlagen.

Nachtrag

Um in freier Wildbahn verwendet zu werden, muss der Compiler normalerweise intern herausfinden, wie er die funktionale Version in die prozedurale Version umwandelt, da der Funktionsaufruf-Overhead zu hoch ist.Rekursive Fälle wie die gezeigte Fakultät verwenden Tricks wie Rückruf um die O(n)-Speichernutzung zu entfernen.Die Tatsache, dass es keine Nebenwirkungen gibt, ermöglicht es funktionalen Compilern, das zu implementieren && ret Optimierung, auch wenn die .reduce wird zuletzt erledigt.Die Verwendung von Lodash in JS ermöglicht offensichtlich keine Optimierung und beeinträchtigt daher die Leistung (was bei der Webentwicklung normalerweise kein Problem darstellt).Sprachen wie Rust werden intern optimiert (und haben Funktionen wie try_fold zu helfen && ret Optimierung).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top