La programmation fonctionnelle est-elle simplement différente ou est-elle réellement plus difficile?

12

La programmation fonctionnelle est-elle simplement différente ou est-elle réellement plus difficile ?

Dites quelqu'un qui n'a jamais appris la programmation et qui apprend la programmation fonctionnelle. vs quelqu'un qui n'a jamais appris la programmation du tout auparavant et qui apprend la programmation impérative. lequel trouvera-t-il plus difficile? ou pareil?

Ma question: disons que le problème est maintenant de camel case une entrée,

tel qui qwe_asd_zxc_rty_fgh_vbndevientqweAsdZxcRtyFghVbn

La voie procédurale est la suivante:

  1. le diviser le long de la _
  2. parcourir le tableau en sautant le premier élément
  3. pour chaque entrée, nous mettons en majuscule la première lettre
  4. joindre les résultats ensemble

La manière fonctionnelle est:

  1. si je ne trouve pas le _retourinput
  2. couper le inputlong du premier _(de telle sorte que nous obtenons qweet asd_zxc_rty_gfh_cvb)
  3. mettre en majuscule la première lettre de headet concaténer cela avecf(tail)

Ok, si vous avez un arrière-plan fonctionnel ET avez une expérience substantielle en programmation procédurale, je voudrais demander: cela vous prendra-t-il plus de temps pour comprendre la manière procédurale ou cela vous prendra-t-il plus de temps pour comprendre la manière fonctionnelle?

Si vous avez des antécédents procéduraux mais avez de nombreuses années d'expérience avec la programmation fonctionnelle, je voudrais poser la même question: cela vous prendra-t-il plus de temps pour comprendre la manière procédurale ou cela vous prendra-t-il plus de temps pour comprendre le fonctionnel façon?

Pacerier
la source
5
Ehrm, la "voie procédurale" me semble parfaitement fonctionnelle si nous utilisons mappour l'étape 3 au lieu d'une boucle de mutation. La deuxième approche est quelque chose que je ne considérerais que s'il n'y a pas de fonction fractionnée dans la bibliothèque standard (auquel cas elle doit être comparée à une solution impérative qui n'utilise pas non plus split).
sepp2k
8
Il n'y a rien de spécifiquement fonctionnel ou procédural dans l'un ou l'autre de vos exemples. Il me semble que vous essayez d'extrapoler à partir d'une mauvaise compréhension de la programmation fonctionnelle. Pas étonnant que vous obteniez des résultats inhabituels.
Rein Henrichs
Je ne pense pas que la programmation fonctionnelle soit difficile, c'est juste différent. Si vous n'avez aucune expérience en programmation, vous êtes tout aussi facile à apprendre, mais pour une raison quelconque, une fois que vous avez appris la programmation impérative, la programmation fonctionnelle devient difficile.
dan_waterworth
1
Je pense que la distinction entre fonctionnel et procédural devient sans objet, car les langages de maintenance comme JavaScript, C #, Groovy contiennent de plus en plus de fonctionnalités fonctionnelles.
user281377
2
La programmation impérative est beaucoup plus difficile et contre-intuitive pour ceux qui n'avaient aucune expérience de programmation précédente. Une expression comme x=x+1peut faire exploser un cerveau inattendu. La programmation fonctionnelle est naturelle, elle n'est rien de plus que des fonctions strictement mathématiques pures et convinientes.
SK-logic

Réponses:

12

Juste différent. La programmation fonctionnelle est beaucoup plus étroitement liée aux mathématiques, que la plupart des gens connaissent. L'ensemble des «variables immuables» ne fait que choquer les programmeurs impératifs où la mentalité «mutable» est profondément enracinée.

Pour les nouveaux arrivants, il est souvent assez intuitif de ne pas simplement changer la valeur de quelque chose.

Là où j'ai étudié le CS, on nous a enseigné une langue fonctionnelle comme notre tout premier cours. Et tous ceux qui avaient déjà appris le C ++ ou Java ont eu du mal avec. Ceux qui ne connaissaient pas la programmation l'ont compris assez facilement.

jalf
la source
jalf êtes-vous de ceux qui débutent dans la programmation et l'avez-vous ramassé assez facilement?
Pacerier
J'étais quelque part entre les deux. J'avais fouillé un peu avec C ++ et un peu de PHP avant, mais pas assez pour vraiment m'habituer à l'état d'esprit impératif. Le schéma était assez clair quand on regardait la classe dans son ensemble. De plus, c'était il y a près d'une décennie, donc non, je ne suis pas tout à fait nouveau dans la programmation aujourd'hui;)
jalf
Variables immuables? Mathématique n'est-il pas un langage de programmation fonctionnel? les variables en mathématique sont sûrement mutables, non?
user56834
20

C'est juste différent

Lorsque vous programmez, vous traduisez essentiellement la façon dont vous raisonnez en code, la distance entre vos pensées et la solution finale pourrait être considérée comme le «fossé cognitif». Plus l'écart est grand, plus il sera difficile pour vous de le combler.

Si vous venez d'un milieu procédural, vous vous serez entraîné à penser de manière procédurale, de sorte que l'écart sera moindre que pour le code fonctionnel, et vice versa.

La seule façon pour un paradigme de programmation d'être intrinsèquement plus facile que toute autre chose serait de le mapper à quelque chose que vous connaissiez déjà, comme un langage ordinaire, de sorte que vous commenciez par un intervalle plus court.

Fonctionnel et procédural est un concept assez fluide de toute façon et ils ont tendance à se chevaucher

Homde
la source
4

Oui, la programmation fonctionnelle a tendance à être difficile à comprendre pour beaucoup de gens (j'aurais tendance à dire, en particulier ceux qui ont déjà été exposés à la programmation procédurale en premier).

Je dirais également que votre exemple de programmation fonctionnelle n'est pas vraiment un très bon exemple de programmation fonctionnelle. Il utilise la récursivité et compose simplement un résultat au lieu de modifier l'état, mais pas beaucoup plus que cela.

Pour obtenir un meilleur exemple de programmation fonctionnelle, considérez un problème plus général: plutôt que "rechercher un trait de soulignement et convertir la lettre suivante en majuscule", considérez ceci comme un cas particulier de recherche d'un modèle et d'exécution de code arbitraire lorsque c'est trouvé.

Beaucoup de langages le supportent, mais pour ce faire, ils nécessitent que nous spécifions le modèle comme quelque chose comme une expression régulière. Cependant, les expressions régulières ne sont ni plus ni moins qu'un langage de programmation à usage spécial, et une implémentation RE est un compilateur et / ou un interprète pour ce langage. Le résultat de la compilation du RE est fondamentalement une fonction qui s'exécute (dans une machine virtuelle RE spéciale) pour faire correspondre l'expression à une entrée.

Dans quelque chose comme Perl, vous utilisez un langage spécial pour spécifier le modèle, et un compilateur spécial pour convertir cette chaîne en une sorte de chose semblable à une fonction, et un interpréteur spécial pour prendre cette chose semblable à une fonction et l'exécuter. Dans un langage fonctionnel, vous utilisez généralement le langage lui-même pour spécifier le modèle et utilisez le propre compilateur du langage pour produire une fonction réelle . Nous pouvons générer cette fonction à la volée (à peu près comme nous pouvons compiler une RE quand nous le voulons), mais quand nous le faisons, le résultat peut s'exécuter comme n'importe quelle autre fonction dans le langage au lieu d'avoir besoin de choses RE spéciales pour le faire.

Le résultat est que nous pouvons généraliser le problème ci-dessus relativement facilement. Au lieu de coder en dur le '_' et le "majuscule" directement dans la transformation, cependant, nous pouvons avoir quelque chose comme:

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

Mais, contrairement à quelque chose où nous spécifions le modèle en tant qu'ER, nous pouvons spécifier le modèle directement en tant que fonction réelle, et toujours l'utiliser, quelque chose comme:

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

Et puis nous passons cette fonction au s & r. En ce moment, c'est une fonction assez banale, et nous l'avons encodée entièrement statiquement. Un langage fonctionnel devient largement intéressant lorsque nous l'utilisons comme nous le pouvons, et générons une fonction entièrement nouvelle à la volée basée sur quelque chose comme l'entrée utilisateur, mais contrairement à une RE, cette fonction n'a pas besoin d'un interpréteur RE spécial pour fonctionner - c'est juste une fonction normale comme une autre.

Jerry Coffin
la source
4

Voici le code complet dans 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"

En tant que programmeur fonctionnel avec une expérience procédurale, je ne pense pas qu'il me faudrait plus de temps pour «trouver» une solution procédurale, mais il me faudrait certainement plus de temps pour la taper.

BTW, l'exemple de résultat attendu dans le message d'origine est faux: il manque un "h" vers la fin.

Ryan Culpepper
la source
gd pour le souligner. édité
Pacerier
3

Ma théorie de base est que les modèles de programmation sont plus faciles à comprendre plus ils sont proches du fonctionnement réel des ordinateurs. Les pointeurs sont difficiles à comprendre jusqu'à ce que vous vous rendiez compte qu'il s'agit essentiellement d'adresses de machines. La récursivité est difficile à comprendre jusqu'à ce que vous ayez consciemment parcouru un petit exemple, vu les cadres de pile et réalisé où les différentes valeurs de la même variable sont stockées. Cela ne signifie pas que la programmation par assembleur est plus facile que la programmation de haut niveau, mais avoir vu comment cela se fait fait des merveilles pour le modèle mental qui est essentiel à la compétence - que ce soit en programmation ou en général.

Maintenant, le modèle procédural est un peu plus proche de l'architecture habituelle de la machine: les affectations sont des écritures en mémoire (ou registre). Les appels de procédure ne sont en fait que des sauts fantaisistes, un ifest en fait un saut conditionnel, etc. Mais en Lisp, par exemple, il n'y a pas d'équivalent bas niveau simple à une liaison lexicale ou à une expression lambda. Pour le comprendre, vous devez imaginer une machine fonctionnelle abstraite complètement distincte entre le niveau de langage et la machine physique, car et apparemment, la plupart des gens ne vont jamais aussi loin.

(Je suis familier avec l'idée que l'architecture de von Neumann est finalement arbitraire, et nous ne devrions pas l'esprit des débutants préjugés avec des détails non pertinents de l' architecture de la machine, et au lieu directement les présenter à la sémantique des langages de programmation. En fait, je l' ai J'ai enseigné quelques-uns de ces cours moi-même. Mais de plus en plus, je pense que c'est un objectif noble mais erroné; les gens apprennent la programmation en renforçant la compréhension de bas en haut, et le chemin vers la programmation fonctionnelle est tout simplement un peu plus long.)

Kilian Foth
la source
7
Par cette logique, l'assembleur devrait être la langue la plus facile à apprendre :)
Homde
4
La programmation fonctionnelle est assez facile à comprendre si vous y arrivez dans la bonne direction. La "machine fonctionnelle abstraite" que vous mentionnez est simplement l' algèbre . L'évaluation se fait par réécriture des termes; l'application de la fonction se fait par substitution. Les programmeurs apprennent à résoudre des problèmes en utilisant les mêmes outils qu'ils ont déjà vus depuis des années dans les classes de mathématiques. S'ils poursuivent CS, il y a suffisamment de temps pour rencontrer la pile de tortues plus tard; sinon, ils ont encore acquis des compétences utiles en résolution de problèmes et des principes de conception. Jetez un œil à Comment concevoir des programmes .
Ryan Culpepper
2
@mko Non, selon cette logique, les bytecodes réels 011011001001101...seraient la langue la plus facile à apprendre!
MarkJ
2
@konrad: l'assembleur RISC EST probablement la langue la plus facile à apprendre. Savoir comment faire quelque chose d'utile est une autre histoire tous ensemble ...
Bryan Boettcher