Unterscheidet sich die funktionale Programmierung oder ist sie wirklich schwieriger?

12

Unterscheidet sich die funktionale Programmierung oder ist sie wirklich schwieriger ?

Sagen wir, jemand, der noch nie Programmieren gelernt hat und in funktionalem Programmieren unterrichtet wird. gegen jemanden, der das Programmieren noch nie erlernt hat und der das Imperative Programmieren gelernt hat. was wird er härter finden? oder das selbe?

Meine frage: sag mal das problem ist jetzt einen eingang zu camel-case,

so qwe_asd_zxc_rty_fgh_vbnwird dasqweAsdZxcRtyFghVbn

Der prozedurale Weg ist:

  1. Teilen Sie es entlang der _
  2. Durchlaufen Sie das Array und überspringen Sie das erste Element
  3. Für jeden Eintrag wird der erste Buchstabe in Großbuchstaben geschrieben
  4. Füge die Ergebnisse zusammen

Die Funktionsweise ist:

  1. Wenn keine _Rückgabe möglich istinput
  2. schneide die inputentlang der ersten _(so dass wir bekommen qweund asd_zxc_rty_gfh_cvb)
  3. Schreiben Sie den ersten Buchstaben von headgroß und schreiben Sie ihn mitf(tail)

Ok, wenn Sie einen funktionalen Hintergrund und umfangreiche Erfahrung in der prozeduralen Programmierung haben, möchte ich Sie fragen: Werden Sie länger brauchen, um den prozeduralen Weg zu finden, oder werden Sie länger brauchen, um den funktionalen Weg zu finden?

Wenn Sie einen prozeduralen Hintergrund haben, aber langjährige Erfahrung mit funktionaler Programmierung haben, möchte ich dieselbe Frage stellen: Werden Sie länger brauchen, um den prozeduralen Weg zu finden, oder werden Sie länger brauchen, um das Funktionale herauszufinden? Weg?

Pacerier
quelle
5
Ehrm, der "prozedurale Weg" scheint mir perfekt zu funktionieren, wenn wir mapfür Schritt 3 anstelle einer mutierenden Schleife verwenden. Der zweite Ansatz würde ich nur in Betracht ziehen, wenn es in der Standardbibliothek keine Teilungsfunktion gibt (in diesem Fall sollte er mit einer Imperativlösung verglichen werden, die ebenfalls keine verwendet split).
2.
8
Keines Ihrer Beispiele ist spezifisch funktional oder prozedural. Es hört sich für mich so an, als würden Sie versuchen, aus einem fehlerhaften Verständnis der funktionalen Programmierung zu extrapolieren. Kein Wunder, dass Sie ungewöhnliche Ergebnisse erzielen.
Rein Henrichs
Ich denke nicht, dass funktionale Programmierung schwierig ist, sie ist einfach anders. Wenn Sie ebenfalls keine Programmiererfahrung haben, sind diese ebenso leicht zu erlernen, aber aus irgendeinem Grund wird die funktionale Programmierung schwierig, wenn Sie erst einmal das Imperative Programmieren gelernt haben.
dan_waterworth
1
Ich denke, die Unterscheidung zwischen funktional und prozedural wird strittig, da gängige Sprachen wie JavaScript, C # und Groovy immer mehr Funktionsmerkmale enthalten.
user281377
2
Imperative Programmierung ist für diejenigen, die noch keine Programmiererfahrung hatten, viel schwieriger und kontraintuitiver. Ein Ausdruck wie x=x+1kann ein unerwartetes Gehirn in die Luft jagen. Funktionale Programmierung ist selbstverständlich, sie ist nichts anderes als reine und zweckmäßige rein mathematische Funktionen.
SK-logic

Antworten:

12

Einfach anders. Die funktionale Programmierung ist viel enger mit der Mathematik verwandt, mit der die meisten Menschen vertraut sind. Die ganze Sache mit den "unveränderlichen Variablen" ist nur für imperative Programmierer ein Schock, bei denen die "veränderliche" Denkweise tief verwurzelt ist.

Für Neulinge ist es oft ziemlich intuitiv, dass man den Wert von etwas nicht einfach ändern kann.

Während meines CS-Studiums wurde uns als allererster Kurs eine funktionale Sprache beigebracht. Und jeder, der zuvor C ++ oder Java gelernt hatte, hatte damit zu kämpfen. Diejenigen, die neu in der Programmierung waren, nahmen es ziemlich leicht auf.

jalf
quelle
jalf bist du einer von denen, die neu in der Programmierung sind und es ziemlich leicht verstanden haben?
Pacerier
Ich war irgendwo dazwischen. Ich hatte vorher ein bisschen mit C ++ und etwas PHP herumgespielt, aber nicht genug, um mich wirklich an die imperative Denkweise zu gewöhnen. Das Muster war ziemlich klar, als Sie die Klasse als Ganzes betrachteten. Außerdem ist dies fast ein Jahrzehnt her, also nein, ich bin nicht ganz neu in der Programmierung heute;)
Jalf
Unveränderliche Variablen? Ist Mathematica nicht eine funktionale Programmiersprache? Variablen in Mathematica sind sicherlich veränderlich, nicht wahr?
user56834
20

Es ist einfach anders

Wenn Sie programmieren, übersetzen Sie im Wesentlichen die Art und Weise, wie Sie argumentieren, in Code. Die Distanz zwischen Ihren Gedanken und der endgültigen Lösung kann als "kognitive Lücke" bezeichnet werden. Je größer die Lücke, desto schwieriger wird es für Sie, sie zu überbrücken.

Wenn Sie einen prozeduralen Hintergrund haben, haben Sie sich darin geschult, prozedural zu denken, sodass die Lücke geringer ist als bei funktionalem Code und umgekehrt.

Die einzige Möglichkeit, ein Programmierparadigma von sich aus einfacher zu gestalten als alles andere, besteht darin, es auf etwas abzubilden, das Sie bereits kannten, wie z.

Funktionell und prozedural ist sowieso ein ziemlich fließendes Konzept und sie neigen dazu, sich zu überschneiden

Homde
quelle
4

Ja, funktionale Programmierung ist für viele Menschen schwer nachvollziehbar (ich würde eher sagen, besonders für diejenigen, die zuvor bereits mit prozeduraler Programmierung konfrontiert waren).

Ich würde auch sagen, dass Ihr Beispiel für funktionale Programmierung kein wirklich gutes Beispiel für funktionale Programmierung ist. Es wird eine Rekursion verwendet und nur ein Ergebnis erstellt, anstatt den Status zu ändern, aber nicht viel mehr.

Um ein besseres Beispiel für funktionale Programmierung zu erhalten, betrachten Sie ein allgemeineres Problem: Anstatt nach einem Unterstrich zu suchen und den nächsten Buchstaben in Großbuchstaben umzuwandeln, betrachten Sie dies als einen Sonderfall, bei dem nach einem Muster gesucht und ein beliebiger Code ausgeführt wird, wenn es ist gefunden.

Viele Sprachen unterstützen dies, aber dazu müssen wir das Muster als einen regulären Ausdruck definieren. Reguläre Ausdrücke sind jedoch nichts anderes als eine spezielle Programmiersprache, und eine RE-Implementierung ist ein Compiler und / oder Interpreter für diese Sprache. Das Ergebnis des Kompilierens der RE ist im Grunde eine Funktion, die (in einer speziellen virtuellen RE-Maschine) ausgeführt wird, um den Ausdruck mit einer Eingabe abzugleichen.

In etwas wie Perl verwenden Sie eine spezielle Sprache, um das Muster anzugeben, und einen speziellen Compiler, um diesen String in eine Art funktionsähnliches Element zu konvertieren, und einen speziellen Interpreter, um dieses funktionsähnliche Element zur Ausführung zu bringen. In einer funktionalen Sprache verwenden Sie normalerweise die Sprache selbst, um das Muster anzugeben, und verwenden den eigenen Compiler der Sprache, um eine echte Funktion zu erstellen . Wir können diese Funktion im laufenden Betrieb generieren (etwa so, als könnten wir eine RE kompilieren, wenn wir wollen), aber wenn wir dies tun, kann das Ergebnis wie jede andere Funktion in der Sprache ausgeführt werden, anstatt dafür spezielle RE-Funktionen zu benötigen.

Das Ergebnis ist , dass wir können das Problem oben relativ leicht verallgemeinern. Anstatt das '_' und "Großbuchstaben" direkt in die Transformation zu codieren, können wir jedoch Folgendes haben:

s&r(pattern, transform, string) {
    if (!pattern(string))
        return string
    else
        return transform(matched part of string) + s&r(rest of string);
}

Aber im Gegensatz zu etwas, bei dem wir das Muster als RE spezifizieren, können wir das Muster direkt als echte Funktion spezifizieren und es trotzdem verwenden, so etwas wie:

my_pattern(string) return beginning(string) == '_';

Und dann übergeben wir diese Funktion dem s & r. Im Moment ist es eine ziemlich triviale Funktion und wir haben sie vollständig statisch codiert. Eine funktionale Sprache wird zum größten Teil interessant, wenn wir sie so verwenden, als könnten wir REs erstellen und eine völlig neue Funktion erstellen, die auf Benutzereingaben basiert. Im Gegensatz zu einer RE benötigt diese Funktion jedoch keinen speziellen RE-Interpreter Nur eine normale Funktion wie jede andere.

Jerry Sarg
quelle
4

Hier ist der vollständige Code in Racket :

;; camelize : string -> string
(define (camelize str)
  (let ([parts (regexp-split #rx"_" str)])
    ;; result of regexp-split is never empty
    (apply string-append
           (first parts)
           (map string-titlecase (rest parts)))))

(camelize "qwe_asd_zxc_rty_fgh_vbn")
;; => "qweAsdZxcRtyFghVbn"

Als funktionaler Programmierer mit prozeduraler Erfahrung glaube ich nicht, dass ich länger brauchen würde, um eine prozedurale Lösung "herauszufinden", aber ich würde sicherlich länger brauchen, um sie einzutippen.

Übrigens ist das erwartete Beispielergebnis im ursprünglichen Beitrag falsch: Am Ende fehlt ein "h".

Ryan Culpepper
quelle
gd für den Hinweis darauf. bearbeitet
Pacerier
3

Meine Haustier-Theorie ist, dass Programmiermodelle leichter zu verstehen sind, je näher sie an der tatsächlichen Arbeitsweise von Computern liegen. Zeiger sind schwer zu verstehen, bis Sie erkennen, dass es sich im Wesentlichen um Computeradressen handelt. Die Rekursion ist schwer zu verstehen, bis Sie bewusst ein kleines Beispiel durchgegangen sind, die Stapelrahmen gesehen und erkannt haben, wo die verschiedenen Werte derselben Variablen gespeichert sind. Das bedeutet nicht, dass Assembler-Programmierung einfacher ist als High-Level-Programmierung, aber zu sehen, wie es gemacht wird, wirkt sich positiv auf das mentale Modell aus, das den Schlüssel zur Kompetenz darstellt - ob in der Programmierung oder in der allgemeinen Benutzerfreundlichkeit.

Das Vorgehensmodell ist der üblichen Maschinenarchitektur etwas näher gekommen: Zuweisungen sind Speicher- (oder Registerschreibvorgänge). Prozeduraufrufe sind eigentlich nur ausgefallene Sprünge, ein ifbedingter Sprung usw. In Lisp zum Beispiel gibt es kein einfaches Low-Level-Äquivalent zu einer lexikalischen Bindung oder einem Lambda-Ausdruck. Um dies zu verstehen, müssen Sie sich eine völlig separate abstrakte funktionale Maschine zwischen der Sprachebene und der physischen Maschine vorstellen, da und anscheinend die meisten Menschen nie so weit kommen.

(Ich bin vertraut mit der Idee , dass die von Neumann - Architektur letztlich willkürlich ist, und wir sollen nicht Vorurteil Anfänger Köpfe mit solchen irrelevanten Details der Maschinenarchitektur und stattdessen direkt führen sie in die Semantik von Programmiersprachen. In der Tat, ich habe Ich habe selbst ein paar solcher Kurse unterrichtet, aber immer mehr empfinde ich dies als ein nobles, aber fehlgeleitetes Ziel: Die Leute lernen das Programmieren, indem sie das Verständnis von unten aufbauen, und der Weg zum funktionalen Programmieren ist einfach etwas länger.)

Kilian Foth
quelle
7
Nach dieser Logik sollte Assembler die am einfachsten zu erlernende Sprache sein :)
Homde
4
Funktionale Programmierung ist leicht zu verstehen, wenn man sie aus der richtigen Richtung betrachtet. Die "abstrakte funktionale Maschine", die Sie erwähnen, ist einfach Algebra . Die Bewertung erfolgt durch Umschreiben von Begriffen. Funktionsanwendung erfolgt durch Substitution. Programmierer lernen, Probleme mit den gleichen Werkzeugen zu lösen, die sie bereits in jahrelangen Matheklassen gesehen haben. Wenn sie CS verfolgen, bleibt genügend Zeit, um den Schildkrötenstapel später zu treffen. Wenn nicht, haben sie immer noch nützliche Fähigkeiten zur Problemlösung und Gestaltungsprinzipien erlernt. Schauen Sie sich an, wie Sie Programme entwerfen .
Ryan Culpepper
2
@mko Nein, nach dieser Logik 011011001001101...wären die tatsächlichen Bytecodes die am einfachsten zu erlernende Sprache!
MarkJ
2
@konrad: RISC Assembler ist wahrscheinlich die am einfachsten zu erlernende Sprache. Zu wissen, wie man es dazu bringt, etwas Nützliches zu tun, ist eine andere Geschichte ...
Bryan Boettcher