• [^] # Re: Lisibilité

    Posté par . En réponse au journal Portage de TapTempo en OCaml. Évalué à 4. Dernière modification le 15 mars 2018 à 11:45.

    Kantien pour sa brillante réponse.

    Merci.

    C’est cette progressivité qui me manque souvent dans la documentation sur OCaml.

    Tu parles de la documentation officielle ? Celle-ci est plus un manuel de référence du langage qu'une initiation aux principes de la programmation fonctionnelle.

    La traduction de la boucle while que j'ai faite relève des principes généraux de la programmation fonctionnelle. C'est pour cela que j'avais donné un lien vers un de mes commentaires sur le journal de la version de taptempo en Emacs Lisp. Une personne cherchait à écrire la fonction factorielle de manière récursive terminale et ne savait pas comment faire. Il avait écrit la version naïve :

    let rec fact_non_tailrec = function
    | 0 -> 1
    | n -> n * fact_non_tailrec (n - 1)

    qui génère un dépassement de pile sur de grandes entrées :

    fact_non_tailrec 100_000_000;;
    Stack overflow during evaluation (looping recursion?).

    La version impérative pour une telle fonction, à base de boucle for, est la suivante :

    let fact_for_loop n =
     let res = ref 1 in
     for i = n downto 1 do res := !res * i done;
     !res

    La version fonctionnelle avec récursion terminale consiste donc à utiliser un boucle avec un accumulateur, comme dans la version impérative :

    let factorielle n =
     let rec loop res = function
     | 0 -> res
     | n -> loop (n * res) (n-1)
     in loop 1 n

    Dans les deux versions, impérative et fonctionnelle, la boucle dépend de l'entrée n. Mais, dans la version fonctionnelle, l'accumulateur est également un paramètre de la boucle, là où c'est une variable globale pour celle-ci dans le cas impératif.

    Pour l'autre transformation du code, là c'est plutôt une astuce propre aux langages fonctionnels qui permettent d'avoir des opérateurs binaires infixes, donc hors famille Lisp et leur folie des parenthèses. L'idée étant que dans une telle situation :

    step1 x; step2 x; step3 x

    on puisse « factoriser » la variable sur laquelle on effectue notre séquence de transformation :

    (step1 & step2 & step3) x
    (* ou en chaînant à la manière d'une pipeline *)
    x |- step1 |- step2 |- step3

    Ici, il faut pouvoir définir les opérateurs d'ordre supérieur & et |-, ce que peut faire n'importe quel langage fonctionnel, mais leur utilité réside essentiellement dans le fait qu'on les utilise de manière infixe, ce qui fournit du sucre syntaxique à cette approche.

    Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.