Ich habe mich entschlossen, funktionale Programmierung zu erlernen. Bisher war es eine Explosion und ich habe das Licht sozusagen gesehen. Leider kenne ich keinen funktionalen Programmierer, von dem ich Fragen abprallen kann. Einführung in Stack Exchange.
Ich nehme an einem Web- / Softwareentwicklungskurs teil, aber mein Kursleiter ist nicht mit funktionaler Programmierung vertraut. Er ist in Ordnung, wenn ich es benutze, und er hat mich gebeten, ihm zu erklären, wie es funktioniert, damit er meinen Code besser lesen kann.
Ich entschied, dass der beste Weg, dies zu tun, darin besteht, eine einfache mathematische Funktion zu veranschaulichen, wie das Erhöhen eines Wertes zu einer Potenz. Theoretisch könnte ich das leicht mit einer vorgefertigten Funktion machen, aber das würde den Zweck eines Beispiels zunichte machen.
Wie auch immer, ich habe einige Schwierigkeiten herauszufinden, wie ich einen Wert halten kann. Da dies eine funktionale Programmierung ist, kann ich die Variable nicht ändern. Wenn ich das unbedingt codieren würde, würde es ungefähr so aussehen:
(Das Folgende ist alles Pseudocode)
f(x,y) {
int z = x;
for(int i = 0, i < y; i++){
x = x * z;
}
return x;
}
Bei der funktionalen Programmierung war ich mir nicht sicher. Das habe ich mir ausgedacht:
f(x,y,z){
if z == 'null',
f(x,y,x);
else if y > 1,
f(x*z,y-1,z);
else
return x;
}
Ist das richtig? z
In beiden Fällen muss ich einen Wert halten , aber ich war mir nicht sicher, wie ich das in der Funktionsprogrammierung machen sollte. Theoretisch hat es funktioniert, aber ich war mir nicht sicher, ob es „richtig“ war. Gibt es einen besseren Weg, dies zu tun?
y
.Antworten:
Zunächst herzlichen Glückwunsch zum "Sehen des Lichts". Sie haben die Software-Welt zu einem besseren Ort gemacht, indem Sie Ihren Horizont erweitert haben.
Zweitens gibt es ehrlich gesagt keine Möglichkeit, dass ein Professor, der die funktionale Programmierung nicht versteht, irgendetwas Nützliches über Ihren Code aussagt, abgesehen von tritalen Kommentaren wie "Der Einzug sieht nicht gut aus". Dies ist in einem Webentwicklungskurs nicht so überraschend, da die meisten Webentwicklungen mit HTML / CSS / JavaScript durchgeführt werden. Abhängig davon, wie sehr Ihnen das Erlernen der Webentwicklung wirklich am Herzen liegt, möchten Sie sich möglicherweise anstrengen, um die Tools zu erlernen, die Ihr Professor lehrt (auch wenn es schmerzhaft sein mag - ich weiß aus Erfahrung).
Um die gestellte Frage zu beantworten: Wenn Ihr Imperativcode eine Schleife verwendet, ist der Funktionscode wahrscheinlich rekursiv.
Beachten Sie, dass dieser Algorithmus tatsächlich mehr oder weniger mit dem Imperativcode identisch ist. Tatsächlich könnte man die obige Schleife als syntaktischen Zucker für iterative rekursive Prozesse betrachten.
Als Randnotiz, es gibt keine Notwendigkeit für einen Wert von
z
entweder in Ihrem Imperativ- oder Funktionscode, in der Tat. Sie sollten Ihre Imperativfunktion folgendermaßen geschrieben haben:anstatt die Bedeutung der Variablen zu ändern
x
.quelle
pow
ist nicht ganz richtig. So wie es ist,pow 3 3
kehrt es zurück81
, anstatt27
. Es sollteelse x * pow x (y-1).
Dies ist eigentlich nur ein Nachtrag zur Antwort von gardenhead, aber ich möchte darauf hinweisen, dass es einen Namen für das Muster gibt, das Sie sehen: Falzen.
Bei der funktionalen Programmierung ist eine Falte eine Möglichkeit, eine Reihe von Werten zu kombinieren, die sich einen Wert zwischen den einzelnen Operationen "merken". Erwägen Sie unbedingt das Hinzufügen einer Liste mit Zahlen:
Wir nehmen eine Liste von Werten
xs
und einen Anfangszustand von0
(dargestellt durchtotal
in diesem Fall). Dann wird für jedenx
inxs
kombinieren wir diesen Wert mit dem aktuellen Zustand nach einigen Kombinationsoperation (in diesem Fall zusätzlich), und das Ergebnis als die Verwendung neuer Zustand. Im Wesentlichensum_all([1, 2, 3])
ist äquivalent zu(3 + (2 + (1 + 0)))
. Dieses Muster kann in eine Funktion höherer Ordnung extrahiert werden , eine Funktion, die Funktionen als Argumente akzeptiert:Diese Implementierung von
fold
ist immer noch unerlässlich, kann aber auch rekursiv erfolgen:In Form einer Falte ausgedrückt lautet Ihre Funktion einfach:
... wobei
repeat(x, n)
eine Liste vonn
Kopien von zurückgegeben wirdx
.Viele Sprachen, insbesondere solche, die auf funktionale Programmierung ausgerichtet sind, bieten eine Faltung in ihrer Standardbibliothek an. Sogar Javascript bietet es unter dem Namen
reduce
. Wenn Sie feststellen, dass Sie mithilfe der Rekursion einen Wert über eine Schleife hinweg "merken", möchten Sie im Allgemeinen wahrscheinlich eine Falte.quelle
fold(repeat(base, power), 1, *)
scan
wird im Wesentlichenfold
nicht nur die Werteliste zu einem Wert zusammengefasst, sondern jeder Zwischenwert wird auf dem Weg zurückgespuckt, um eine Liste aller Zwischenzustände zu erstellen, die die Falte erstellt hat, anstatt nur die zu erstellen Endzustand. Es ist implementierbar in Bezug auffold
(jede Schleifenoperation ist).combiner_func
ist ein Argument,sum_all
das eine anonyme Funktion übergibt (das ist daslambda
Bit - es erstellt einen Funktionswert, ohne ihn zu benennen), die definiert, wie zwei Elemente miteinander kombiniert werden sollen.Dies ist eine ergänzende Antwort zur Erläuterung von Karten und Falzen. Für die folgenden Beispiele verwende ich diese Liste. Denken Sie daran, dass diese Liste unveränderlich ist und sich daher niemals ändern wird:
In meinen Beispielen verwende ich Zahlen, da sie zu leicht lesbarem Code führen. Denken Sie jedoch daran, dass Falten für alles verwendet werden können, wofür eine traditionelle imperative Schleife verwendet werden kann.
Eine Map nimmt eine Liste von etwas und eine Funktion und gibt eine Liste zurück, die mit der Funktion geändert wurde. Jedes Element wird an die Funktion übergeben und wird zu dem, was die Funktion zurückgibt.
Das einfachste Beispiel hierfür ist das Hinzufügen einer Nummer zu jeder Nummer in einer Liste. Ich werde Pseudocode verwenden, um es sprachunabhängig zu machen:
Wenn Sie drucken
numbers2
, sehen Sie,[3, 4, 5, 6, 7]
welches die erste Liste ist, bei der jedem Element 2 hinzugefügt wurden. Beachten Sie, dass die zu verwendende Funktionadd-two
angegeben wurdemap
.Fold s sind ähnlich, außer dass die Funktion, die Sie ihnen geben müssen, 2 Argumente annehmen muss. Das erste Argument ist in der Regel der Akkumulator (in einer linken Falte, die am häufigsten sind). Der Akkumulator sind die Daten, die beim Schleifen übertragen werden. Das zweite Argument ist das aktuelle Element der Liste. genau wie oben für die
map
Funktion.Wenn Sie drucken
sum
, sehen Sie die Summe der Zahlen: 15.Hier sind die Argumente
fold
dafür:Dies ist die Funktion, die wir geben die Falte. Die Falte übergibt die Funktion dem aktuellen Akkumulator und dem aktuellen Element der Liste. Was auch immer die Funktion zurückgibt, wird der neue Akku, der beim nächsten Mal an die Funktion übergeben wird. Auf diese Weise "merken" Sie sich Werte, wenn Sie einen Loop im FP-Stil erstellen. Ich habe ihm eine Funktion gegeben, die 2 Zahlen annimmt und sie hinzufügt.
Dies ist der anfängliche Akkumulator. Wie der Akku startet, bevor Elemente in der Liste verarbeitet werden. Wenn Sie Zahlen summieren, wie hoch ist die Gesamtsumme, bevor Sie Zahlen addieren? 0, die ich als zweites Argument übergeben habe.
Schließlich übergeben wir wie bei der Karte auch die Liste der zu verarbeitenden Nummern.
Wenn Falten immer noch keinen Sinn ergeben, ziehen Sie dies in Betracht. Wenn du schreibst:
Sie setzen im Grunde die übergebene Funktion zwischen die einzelnen Elemente in der Liste und fügen den anfänglichen Akkumulator entweder links oder rechts hinzu (je nachdem, ob es sich um eine linke oder eine rechte Falte handelt).
Wird:
Welches entspricht 15.
Verwenden Sie ein,
map
wenn Sie eine Liste in eine andere Liste gleicher Länge umwandeln möchten.Verwenden Sie a,
fold
wenn Sie eine Liste in einen einzelnen Wert umwandeln möchten, z. B. eine Liste von Zahlen summieren.Wie @Jorg in den Kommentaren betonte, muss der "Einzelwert" nicht einfach wie eine Zahl sein. es kann sich um ein einzelnes Objekt handeln, einschließlich einer Liste oder eines Tupels! Die Art und Weise, wie ich tatsächlich Falten hatte, bestand darin, eine Karte in Form einer Falte zu definieren . Beachten Sie, wie der Akku eine Liste ist:
Ehrlich gesagt, wenn Sie erst einmal verstanden haben, werden Sie feststellen, dass fast jede Schleife durch eine Falte oder eine Karte ersetzt werden kann.
quelle
i
nie definiert zu werden), aber ich denke, Sie haben die richtige Idee. Ein Problem mit Ihrem Beispiel ist: der function (x
) wird jeweils nur ein Element der Liste übergeben, nicht die gesamte Liste. Beim erstenx
Aufruf wird Ihr anfänglicher Akku (y
) als erstes Argument und das erste Element als zweites Argument übergeben. Beim nächsten Startx
wird der neue Akku auf der linken Seite (was auch immerx
beim ersten Mal zurückgegeben wurde) und das zweite Element der Liste als zweites Argument übergeben.fold
wenn Sie eine Liste in einen einzelnen Wert umwandeln möchten (wie das Summieren einer Zahlenliste)." - Ich möchte nur darauf hinweisen, dass dieser "Einzelwert" beliebig komplex sein kann ... einschließlich einer Liste! Tatsächlichfold
handelt es sich um eine allgemeine Iterationsmethode, die alles kann, was Iteration kann. ZBmap
kann trivial ausgedrückt werden alsfunc map(f, l) = fold((xs, x) => append(xs, f(x)), [], l)
Hier ist der "Einzelwert", der von berechnetfold
wird, tatsächlich eine Liste.fold
. Und es muss keine Liste sein, jede Sammlung, die als leer / nicht leer ausgedrückt werden kann, ist ausreichend. Was im Grunde bedeutet, dass jeder Iterator ausreicht. (Ich denke, das Wort "Katamorphismus" wäre zu viel für eine Einführung für Anfänger :-D)Es ist schwer, gute Probleme zu finden, die mit der eingebauten Funktionalität nicht gelöst werden können. Und wenn es eingebaut ist, sollte es als Beispiel für einen guten Stil in der Sprache x dienen.
In haskell zum Beispiel haben Sie bereits die Funktion
(^)
im Prelude.Oder wenn Sie es programmatischer machen wollen
product (replicate y x)
Ich sage nur, dass es schwierig ist, die Stärken eines Stils / einer Sprache zu zeigen, wenn Sie nicht die darin enthaltenen Funktionen verwenden. Es könnte jedoch ein guter Schritt sein, um zu zeigen, wie es hinter den Kulissen funktioniert, aber ich denke, Sie sollten den besten Code in der von Ihnen verwendeten Sprache verwenden und dann der Person von dort helfen, zu verstehen, was bei Bedarf vor sich geht.
quelle
product
nur eine Abkürzungsfunktionfold
mit Multiplikation als Funktion und 1 als Ausgangsargument ist und dass diesreplicate
eine Funktion ist, die einen Iterator (oder eine Liste) erzeugt, wie ich bemerkte über den beiden sind in haskell) im wesentlichen nicht zu unterscheiden, was eine gegebene Anzahl identischer Ausgaben ergibt. Es sollte jetzt leicht verständlich sein, wie diese Implementierung das Gleiche wie die Antwort von @ Jack oben macht, indem nur vordefinierte Sonderfallversionen der gleichen Funktionen verwendet werden, um sie prägnanter zu gestalten.