Wenn meine Funktion die folgenden zwei Anforderungen erfüllt, kann die Funktion Sum
, die die Summe der Elemente in einer Liste zurückgibt, in der das Element für eine bestimmte Bedingung als wahr ausgewertet wird, als reine Funktion bezeichnet werden, nicht wahr ?
1) Für einen gegebenen Satz von i / p wird derselbe o / p unabhängig von der Zeit zurückgegeben, zu der die Funktion aufgerufen wird
2) Es hat keine Nebenwirkungen
public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
int result = 0;
foreach(var item in numbers)
if(predicate(item)) result += item;
return result;
}
Beispiel: Sum(x=>x%2==0, new List<int> {1,2,3,4,5...100});
Der Grund, warum ich diese Frage stelle, ist, dass ich fast überall Leute sehe, die raten, Zuweisungsoperatoren und Schleifen zu vermeiden, weil dies ein zwingender Programmierstil ist. Was kann also mit dem obigen Beispiel schief gehen, das Schleifen und Zuweisungsoperatoren im Kontext der Funktionsprogrammierung verwendet?
quelle
item
Variable in der Schleife mutiert.Antworten:
Was macht die funktionale Programmierung aus?
Die funktionale Programmierung ist grundsätzlich deklarativ . Sie sagen, was Ihr Ergebnis ist, anstatt es zu berechnen.
Werfen wir einen Blick auf die wirklich funktionale Implementierung Ihres Snippets. In Haskell wäre es:
Ist klar, was das Ergebnis ist? Es ist also die Summe der Zahlen, die dem Prädikat entsprechen. Wie wird es berechnet? Es ist mir egal, frag den Compiler.
Man könnte möglicherweise sagen, dass die Verwendung von
sum
undfilter
ein Trick ist und nicht zählt. Lassen Sie es dann ohne diese Helfer implementieren (obwohl der beste Weg wäre, sie zuerst zu implementieren).Die "Functional Programming 101" -Lösung, die nicht verwendet wird,
sum
ist mit Rekursion:Es ist immer noch ziemlich klar, was das Ergebnis in Bezug auf einen einzelnen Funktionsaufruf ist. Es ist entweder
0
oderrecursive call + h or 0
abhängig davonpred h
. Immer noch ziemlich einfach, auch wenn das Endergebnis nicht sofort offensichtlich ist (obwohl dies mit ein wenig Übung wirklich wie einefor
Schleife liest ).Vergleichen Sie das mit Ihrer Version:
Was ist das Ergebnis? Oh, ich verstehe: einzelne
return
Aussage, keine Überraschungen hier :return result
.Aber was ist das
result
?int result = 0
? Scheint nicht richtig. Damit machst du später etwas0
. Ok, du fügstitem
s hinzu. Und so weiter.Für die meisten Programmierer ist dies natürlich ziemlich offensichtlich, was in einer einfachen Funktion wie dieser passiert, aber fügen Sie eine zusätzliche
return
Aussage hinzu, und es wird plötzlich schwieriger zu verfolgen. Im gesamten Code geht es darum, wie und was der Leser noch herausfinden muss - dies ist eindeutig ein sehr zwingender Stil .Sind also Variablen und Schleifen falsch?
Nein.
Es gibt viele Dinge, die durch sie viel einfacher erklärt werden, und viele Algorithmen, die einen veränderlichen Zustand erfordern, um schnell zu sein. Variablen sind jedoch von Natur aus unerlässlich. Sie erklären, wie statt was und geben nur wenige Vorhersagen darüber, wie hoch ihr Wert einige Zeilen später oder nach einigen Schleifeniterationen sein kann. Schleifen erfordern im Allgemeinen einen Zustand, um einen Sinn zu ergeben, und daher sind sie von Natur aus auch zwingend erforderlich.
Variablen und Schleifen sind einfach keine funktionale Programmierung.
Zusammenfassung
Die zeitgemäße funktionale Programmierung ist ein bisschen mehr Stil und eine nützliche Denkweise als ein Paradigma. In dieser Denkweise liegt eine starke Präferenz für die reinen Funktionen, aber es ist eigentlich nur ein kleiner Teil.
In den meisten weit verbreiteten Sprachen können Sie einige funktionale Konstrukte verwenden. In Python können Sie beispielsweise wählen zwischen:
oder
oder
Diese funktionalen Ausdrücke passen gut zu solchen Problemen und machen den Code einfach kürzer (und kürzer ist gut ). Sie sollten imperativen Code nicht gedankenlos durch sie ersetzen, aber wenn sie passen, sind sie fast immer die bessere Wahl.
quelle
Von der Verwendung eines veränderlichen Zustands wird bei der funktionalen Programmierung im Allgemeinen abgeraten. Schleifen werden daher nicht empfohlen, da Schleifen nur in Kombination mit einem veränderlichen Zustand nützlich sind.
Die Funktion als Ganzes ist rein, was großartig ist, aber das Paradigma der funktionalen Programmierung gilt nicht nur auf der Ebene ganzer Funktionen. Sie möchten auch einen veränderlichen Status auch auf lokaler Ebene innerhalb von Funktionen vermeiden. Und die Argumentation ist im Grunde die gleiche: Das Vermeiden eines veränderlichen Zustands erleichtert das Verständnis des Codes und verhindert bestimmte Fehler.
In Ihrem Fall könnten Sie schreiben,
numbers.Where(predicate).Sum()
was eindeutig viel einfacher ist. Und einfacher bedeutet weniger Fehler.quelle
Where
in ansehenumbers.Where(predicate).Sum()
- es nutzt dieforeach
Schleife.Während Sie richtig sind, dass aus Sicht eines externen Beobachters Ihre
Sum
Funktion rein ist, ist die interne Implementierung eindeutig nicht rein - Sie haben einen Zustand gespeichert, inresult
dem Sie wiederholt mutieren. Einer der Gründe , wandelbar Zustand zu vermeiden ist , weil es eine größere kognitive Belastung auf dem Programmiergerät erzeugt, was wiederum führt zu mehr Fehlern [Bearbeiten] .Während in einem einfachen Beispiel wie diesem die Menge des gespeicherten veränderlichen Zustands wahrscheinlich klein genug ist, um keine ernsthaften Probleme zu verursachen, gilt das allgemeine Prinzip weiterhin. Ein Spielzeugbeispiel wie
Sum
wahrscheinlich ist nicht der beste Weg, um den Vorteil der funktionalen Programmierung gegenüber dem Imperativ zu veranschaulichen. Versuchen Sie, etwas mit viel veränderlichem Zustand zu tun, und die Vorteile werden möglicherweise klarer.quelle