kantien a écrit 1187 commentaires

  • [^] # Re: À boire et à manger

    Posté par  . En réponse au journal Un développeur qui dénonce. Évalué à 4.

    Le truc qui fait que la gestion des dépendances est NP-complet c’est pas le graphe de dépendance en soi, si je me trompe pas, c’est qu’à chaque dépendance on a le choix entre plusieurs versions de dépendance et les contraintes qui font que certaines de ces versions ne sont pas toutes compatibles entre elles, ce qui nous donne un genre de problème de satisfaction de contraintes.

    Effectivement, c'est ce que je me suis dit après avoir écrit le message, la NP-complétude vient peut être de la prise en compte des conflits entre paquets (ce qui revient à gérer la négation logique dans le problème SAT sous-jacent).

    Après que les compilateurs effectuent déjà de l'élimination de code mort, je le sais bien. Mon interrogation, dans la lignée de celle d'arnaudus, est plutôt de savoir à quel point il est difficile de ne garder que ce qui est nécessaire et suffisant pour faire tourner le binaire, ni plus ni moins.

    Dans l'exemple que j'ai écrit, il me semble bien que le compilateur OCaml va me lier statiquement tout le module List bien qu'une grande partie de son code ne soit pas utilisé. Dans le cas d'un langage orienté objet, si mon binaire n'utilise pas toutes les méthodes d'un objet, il n'est pas nécessaire de compiler les méthodes non utilisées. Mais à quel point cela peut-il perturber le schème de compilation pour le code d'un objet ?

    Quand on fait de la liaison dynamique, on reporte la difficulté sur le gestionnaire de paquet qui doit alors gérer un problème NP-complet (mais c'est sans doute lié au problème de conflits, qui n'existe pas pour la liaison statique). En revanche, pour la liaison statique, ce n'est pas sûr que le problème soit tellement plus simple si on ne veut garder que ce qui est absolument nécessaire, ni plus ni moins, et résoudre la question dans son entière généralité.

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

  • [^] # Re: À boire et à manger

    Posté par  . En réponse au journal Un développeur qui dénonce. Évalué à 7.

    Je ne suis pas sûr qu'il soit aussi simple de détecter les bouts de code qui ne seront jamais appelés

    À première vue, je dirais que le problème est NP-complet.

    let f l = List.map (fun i -> i + 2) l

    Ici, la fonction f dépend à la fois de la fonction map du module List ainsi que de la fonction (+), fonctions qui elles-mêmes peuvent avoir d'autres dépendances en amont. Le graphe de dépendances est du même genre que celui d'un gestionnaire de paquets logiciels pour une distribution. Or, il est connu que ce problème se ramène à un problème SAT (voir projet mancoosi) qui est NP-complet.

    Après, si les compilateurs se mettent tous à embarquer des solveurs SAT (même optimisés pour les graphes de dépendances que l'on trouve dans du code idiomatique), il y en a qui vont se plaindre que la compilation dure trois plombes et que les compilos modernes sont tous bloated. :-P

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

  • [^] # Re: L'analyse au delà de l'agrégation

    Posté par  . En réponse à la dépêche Le Frido 2018, livre libre de mathématique pour l’agrégation. Évalué à 3.

    Ce n'est pas plus facile de partir de l'axiome “il existe un ensemble infini”?

    Non, pas vraiment. En fait le processus qu'il décrit est celui de la définition d'un ordinal, cela afin de développer la théorie des ordinaux et l'arithmétique transfinie de Cantor dans ZF. Puis l'axiome de l'infini consiste à affirmer qu'il existe un ordinal limite (i.e. qui n'est pas le successeur d'un autre ordinal, mais l'union des ses antécédents par la relation d'ordre que définit l'appartenance sur eux), ce qui revient tout simplement à affirmer, de manière détournée, que l'arithmétique de Peano est cohérente (elle a un modèle, à savoir le plus petit ordinal limite que l'on appelle \omega).

    J'ai pas trouvé le temps d'écrire ce que je voulais faire, ça attendra un peu. Mais juste pour montrer que ZF est un peu étrange sur sa notion d'ensemble, au niveau de l'arithmétique standard on se retrouve avec ça :

    Tout est ensemble : un entier est un ensemble, les ensembles ne sont composés que d'ensembles. Il n'y a rien d'autres que des ensembles : les ensembles et leurs éléments sont considérés comme des entités homogènes. Un modèle de ZF est un module U qui satisfait cette signature :

    module type ZF = sig
      type t
      val eq : t -> t -> bool
      val appartient : t -> t -> bool
    end

    Dans ZF, on ne peut formuler une proposition comme : tout entier est pair ou impair, mais seulement : pour tout objet, si c'est un entier alors il est pair ou impair.

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

  • [^] # Re: L'analyse au delà de l'agrégation

    Posté par  . En réponse à la dépêche Le Frido 2018, livre libre de mathématique pour l’agrégation. Évalué à 3. Dernière modification le 14 septembre 2018 à 00:45.

    Pour les naturels, j'ai en fait un doute.

    Si, on les définissait ainsi :

    type naturel = Zero | Succ of nat

    Là je l'ai écrit avec la syntaxe OCaml, mais l'idée était la même : une entier est soit zéro, soit le successeur d'un entier. Avec comme principe fondamental pour raisonner sur ce concept, le principe du raisonnement par récurrence (ou de manière équivalente, tout sous-type des entiers a un plus petit élément).

    Pour donner une référence, c'est ainsi que Poincaré présentait l'arithmétique dans la Science et l'Hypothèse (ou la Valeur de la Science, je ne sais plus lequel des deux).

    Pour l'encodage des entiers dans ZF et sa théorie des ordinaux, cela a son intérêt mais ZF (la théorie axiomatique des ensembles) est une théorie un peu étrange qui ne manipule pas des ensembles au sens où on l'entend usuellement. Quand tu dis que tu admets sans détails les principes de cette théorie, c'est en fait plutôt ceux de la théorie des types que tu as en tête (bien que tu appelles ensembles ce que cette théorie appelle types).

    Je n'ai pas le temps de développer plus sur le sujet ce soir, mais j'essaierai demain ou dans le week-end.

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

  • [^] # Re: L'analyse au delà de l'agrégation

    Posté par  . En réponse à la dépêche Le Frido 2018, livre libre de mathématique pour l’agrégation. Évalué à 4.

    Petite erreur dans mon développement de Taylor pour l'étude au voisinage de z_0, il fallait lire :

    qui se comporte comme un terme de la forme a_i (z - z_0)^i au voisinage de z_0.

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

  • [^] # Re: L'analyse au delà de l'agrégation

    Posté par  . En réponse à la dépêche Le Frido 2018, livre libre de mathématique pour l’agrégation. Évalué à 4. Dernière modification le 13 septembre 2018 à 02:40.

    À mon époque (une vingtaine d'années), les tribus et la théorie de la mesure de Borel-Lebesgue c'était à partir de la licence; en prépa on ne voyait que les intégrales de Riemann. En revanche les constructions de naturels, fractions, réels… ainsi que le théorème fondamentale de l'algèbre était bien au programme (avec la démonstration).

    Pour la preuve, une esquisse fait juste appel à des résultats de base de topologie et de comportements asymptotiques au voisinage de zéro et de l'infini des polynômes. Pour des complexes assez grand en module, le module d'un polynôme non constant se comporte comme son terme de plus haut degré et tend donc vers l'infini. On se limite donc à l'étude de son module sur un domaine fini. Or toute fonction continue sur un compact admet un minimum : il reste à montrer que ce dernier est bien zéro, et donc que le polynôme admet une racine. Soit z_0 un nombre complexe tel que P(z_0) soit de module minimal. Cette fois, on a P(z) - P(z_0) = a_1 z + a_2 z^2 + ... + a_n z^n qui se comporte au voisinage de z_0 comme le monôme de plus petite puissance (le premier des a_i non nuls, qui existe car le polynôme est non constant). Ainsi, pour un petit rayon, à quelques perturbations près, l'image d'un disque autour de z_0 est un disque. Or si l'image de ce disque n'est pas centrée sur l'origine, i.e. si z_0 n'est pas une racine, alors on peut trouver à l'intérieur un complexe de module plus petit que celui de l'image de z_0, ce qui contredirait sa minimalité. Donc z_0 est une racine du polynôme.

    CQFD.

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

  • [^] # Re: Pourquoi un tiret bas?

    Posté par  . En réponse au journal Ⓒ✙✙ Le tiret bas (underscore) au début des variables membres ?. Évalué à 3. Dernière modification le 19 août 2018 à 23:50.

    En gros, l’idee c’est de désigner le bouzin pour rendre ce genre d’erreurs impossible/très dure a écrire en premier lieu.

    Oui, et c'est ce qui m'a le plus choqué dans l'article : utiliser des conventions syntaxiques sur les noms de variables et non le système de types pour porter des informations sémantiques. Un code non propre ne devrait même pas compiler !

    Pour faire court :

    • les types sont des concepts dont on construit les objets ;
    • les chaînes de caractères se subdivisent en deux catégories : les sures et les autres ;
    • on définit un sous-type pour représenter les chaînes sures ;
    • la fonction write ne doit accepter que des chaînes sures en entrée, sinon le code refuse de compiler.

    Grossièrement, une spécification simplifiée de son problème dans la syntaxe de mon langage de prédilection ressemble à ceci :

    val request : field -> string
    
    module type Safe = sig
      type t = private string
      val encode : string -> t
      val write : t -> unit
    end

    J'ose espérer que l'auteur n'est pas responsable de système critique sur le plan de la sécurité ou il vaudrait mieux qu'il retourne fabriquer du pain industriel : il n'y a rien de plus sale et moins propre que ce qu'il préconise. ;-)

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

  • [^] # Re: Utilisez ce que vous comprenez

    Posté par  . En réponse à la dépêche La monnaie libre pour une économie du Libre. Évalué à 0.

    95% de la monnaie est de la dette. Que se passera t'il si 95% de la monnaie disparaît ? Fin de la consommation, fin du crédit, fin de l'€uro. Croire au remboursement de la dette est un conte de fées. Et s'il est des utopistes ce sont ceux qui depuis si longtemps croient au même conte plutôt qu'aux comptes.

    Je trouve extraordinaire d'être si critique envers une expérience visant à vérifier s'il est possible d'utiliser une monnaie à création positive (donc sans dette), et le l'être si peu avec la monnaie émise par endettement, qui pourtant est tellement déconnante qu'après la crise de 1929 on se retape celle de 2008.

    Bah, d'un autre côté, depuis qu'il y a de l'argent qui circule en ce bas monde il en a toujours était ainsi comme il se doit : l'argent est et sera toujours, conformément à sa nature, une reconnaissance de dette. ;-)

    Vous avez un écu. Que signifie-t-il en vos mains ? Il y est comme le témoin et la preuve que vous avez, à une époque quelconque, exécuté un travail, dont, au lieu de profiter, vous avez fait jouir la société, en la personne de votre client. Cet écu témoigne que vous avez rendu un service à la société, et, de plus, il en constate la valeur. Il témoigne, en outre, que vous n’avez pas encore retiré de la société un service réel équivalent, comme c’était votre droit. Pour vous mettre à même de l’exercer, quand et comme il vous plaira, la société, par les mains de votre client, vous a donné une reconnaissance, un titre, un bon de la République, un jeton, un écu enfin, qui ne diffère des titres fiduciaires qu’en ce qu’il porte sa valeur en lui-même, et si vous savez lire, avec les yeux de l’esprit, les inscriptions dont il est chargé, vous déchiffrerez distinctement ces mots : « Rendez au porteur un service équivalent à celui qu’il a rendu à la société, valeur reçue constatée, prouvée et mesurée par celle qui est en moi-même. »

    Frédéric Bastiat, Maudit Argent.

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

  • [^] # Re: Gratuité des transports

    Posté par  . En réponse au journal Le Bitcoin va-t-il détruire la planète ? Contre‐point. Évalué à 1.

    Honnêtement je ne pense pas que l'abonnement soit le principal frein, avoir une offre de transport bien fichue (horaires larges, bonne fréquence) est plus importante.

    D'autant qu'en Ile-de-France (région donnée en lien dans le message auquel je répondais), les abonnements ne représentent que 30% du financement du service. Ça représente 3 milliards d'impôts à imaginer sur les 10 milliards de frais de fonctionnements. M'enfin la question à laquelle la personne répondait n'était pas, comment financer autrement les transports en commun, mais comment les rendre moins chers, ou plutôt pourquoi ne pas réduire le coût des abonnements ? Si la solution c'est : on prélève les 3 milliards ailleurs, je ne sais toujours pas si je dois rire ou pleurer.

    Après pour les économies réalisées sur la gestion des abonnements, j'aimerais bien savoir combien il reste à l'arrivée sur ces 30%, mais à mon avis c'est peanuts et rien ne dit qu'elles ne seront pas absorbées par un possible surcoût lié à l'amélioration de l'offre (fréquence, horaires plus larges…). Et l'argument peut s'appliquer à d'autres services à usage illimité utilisés par une proportion encore bien plus grande de la population : téléphonie et internet. D'autant que sur ceux-ci, on peut également arguer, comme pour les transports, d'une contribution de la part des entreprises (elles l'utilisent, cela facilite le télétravail…). Pour choisir entre les services que l'on collectivise à 100%, on fait comment : on joue à pile ou face ? ça dépend du sens du vent ? là c'est différent, c'est une priorité écologique (c'est à la mode en ce moment) ? le politique fait une estimation de l'avantage électoraliste de la mesure ?

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

  • [^] # Re: Gratuité des transports

    Posté par  . En réponse au journal Le Bitcoin va-t-il détruire la planète ? Contre‐point. Évalué à -5.

    Et pourquoi pas carrément rendre les transports gratuits pour les usagers comme c'est le cas à Aubagne depuis 2009 ?

    Rien n'est gratuit dans la vie, sauf le don pour celui qui le reçoit. Ce genre de système ne fait que déplaçait la source de financement : c'est financièrement rentable pour l'usager si son voisin qui n'utilise pas les transports prend sur lui une partie de ce qu'il payait auparavant. Le vol collectivement et légalement organisé, voilà un projet de société intéressant. ;-)

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

  • # Pas fort mais en rythme.

    Posté par  . En réponse au journal OpenTango : outil de métrologie réseaux / télécom. Évalué à 7.

    Taper donc pas trop fort svp.

    Ici on ne tape pas fort, mais en rythme. Il existe même, sur la question, un outil de métrologie à la pointe de la technologie et de l'état de l'art, en différentes versions dont une du maître Jedi.

    OpenTango pourrait suivre la même voie avec un portage de la clef usb avec différentes distributions de base. Nul doute que la version Arch Linux remportera la palme étant la seule à promettre de toujours rester à la pointe de l'état de l'art. :-D

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

  • [^] # Re: Commentaire bookmark

    Posté par  . En réponse au journal "Intelligence artificielle", vraiment?. Évalué à 3. Dernière modification le 05 avril 2018 à 17:47.

    Certes, mais la société et le propriétaire de l'animal considèrent que ce dernier est en mesure de pouvoir agir afin d'éviter une telle situation; si elle se produit alors c'est que les mesures de préventions n'avaient pas été prises et le propriétaire est considère a priori comme responsable. Dans le cas de la voiture totalement autonome, en dehors de ne jamais s'en servir (ce qui réduit grandement son utilité), je ne vois pas comment le propriétaire pourrait éviter le risque d'accident.

    Sinon, au sens propre du terme, une machine ne prend pas de décision : c'est là une représentation quelque peu anthropomorphique de ce que l'on nomme improprement1 intelligence artificielle. ;-)


    1. Villani a raison, le terme intelligence est mal choisi pour qualifier ces systèmes. 

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

  • [^] # Re: Commentaire bookmark

    Posté par  . En réponse au journal "Intelligence artificielle", vraiment?. Évalué à 3.

    C'est comme pour n'importe quel outil. La responsabilité va sur le propriétaire sauf si l’outil a un défaut de conception, comme pour les voitures actuelles…

    Le problème ne se pose même pas. Personne n’achèterait une voiture qui conduit comme ça.

    Personne n'achèterait une voiture pour laquelle ils seraient considérés comme responsables en cas d'accident (surtout s'il cause la mort d'un tiers) alors qu'ils ne peuvent rien y faire. ;-)

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

  • [^] # Re: Fonctionnel vs Impératif

    Posté par  . En réponse au lien État des lieux des langages fonctionnels. Évalué à 2. Dernière modification le 04 avril 2018 à 17:52.

    S'il s'agissait de C j'aurais tendance à penser pareil, mais les map ou fold sur des listes deviennent des itérations avec range sur un tableau en Go […]

    Ou alors, pour être plus générique sur la conception des itérateurs, utiliser des foncteurs applicatifs : The Essence of the Iterator Pattern.

    Pour les types somme, je suis assez d'accord, mais c'est pas intrinsèquement fonctionnel comme truc, je dirais.

    Effectivement, voir le chapitre 1 de ce cours de programmation avancée pour la comparaison entre Java et OCaml sur la manière de définir des types algébriques.

    La seule construction que je ne vois pas comment réaliser à la fois efficacement et de façon commode dans un langage impératif un peu haut-niveau, c'est la récursivité terminale entre fonctions mutuellement récursives (comme truc approchant, je vois que le retour de pointeur vers une fonction, qui a le défaut de faire des sauts calculés non prévisibles).

    Avec des goto ? :-P

    Ok, ---> []

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

  • [^] # Re: Association/SARL

    Posté par  . En réponse à la dépêche LinuxFr.org sera impliqué dans le plan français sur l’intelligence artificielle #FranceIA. Évalué à 8. Dernière modification le 01 avril 2018 à 11:37.

    En quoi le passage en SARL est une obligation ?

    Je ne comprends pas ton interrogation. Le statut de Société Anthropomorphique des Rascasses et Limandes était bien sûr le plus adapté pour la SOLE (Search Of Learning Events).

    Pour le chiffre d'affaire qui semble t'inquiéter, le statut prévoit explicitement que s'il est de 42 et que la SOLE réussi à le décomposer en facteurs premiers (exploit inédit à ce jour) la SARL sera exonérée de charges ad vitam æternam pour service rendu à la patrie : cela évitera aux juges d'avoir à demander les clés de chiffrement.

    Nul doute qu'à partir de l'excellence de la base de données dont dispose la SOLE, elle mènera a bien cet exploit. Il paraîtrait même qu'elle aurait déjà trouvé les facteurs 7 et 3 en s'écriant : « en ce septième jour de la semaine, dimanche de Pâques, louons la sainte trinité notre Père, son Fils et le Saint Esprit et célébrons la résurrection de notre Seigneur ». L'équipe en charge de la superviser redoute tout de même une possible dérive vers un délire mystique et cabalistique : une enquête est en cours.

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

  • [^] # Re: Deep learning

    Posté par  . En réponse à la dépêche LinuxFr.org sera impliqué dans le plan français sur l’intelligence artificielle #FranceIA. Évalué à 8. Dernière modification le 01 avril 2018 à 08:51.

    La communauté scientifique est perplexe.

    Afin de lever ses doutes, la communauté scientifique a demandé à l'IA de se représenter elle-même :

    une sole

    Le verdict n'a pas fait une plie : nous avons bien affaire à une intelligence de tout premier ordre. Le risque est grand que l'être humain soit bientôt déclaré obsolète.

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

  • [^] # Re: Icones bureau

    Posté par  . En réponse à la dépêche GNOME 3.28. Évalué à 4.

    étrange, chez moi il n'y avait pas de raccourci défini…

    Aller dans le menu de configuration du clavier, chercher « Masquer toutes les fenêtres normales » dans la section navigation, puis définir le raccourci à Super + D.

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

  • [^] # Re: Icones bureau

    Posté par  . En réponse à la dépêche GNOME 3.28. Évalué à 5.

    Pourtant il doit y avoir une façon de faire cela…j'ai juste jamais cherché!

    Super + h pour cacher (hide) une fenêtre.

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

  • [^] # Re: Ou pas

    Posté par  . En réponse au journal TapTempo en PHP. Évalué à 2.

    Après réflexion, vu les contraintes du programme, le mieux serait sans doute de faire :

    stty raw -echo # raw mode plus générique que-icanon, et sans echo sur la sortie standard

    avant de rentrer dans la boucle, et :

    stty sane
    

    pour rétablir le terminal dans un état sain avant de quitter le programme (cf man stty pour les explications).

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

  • [^] # Re: Lisibilité

    Posté par  . En réponse au journal Portage de TapTempo en OCaml. Évalué à 3.

    Concernant la récursivité en OCaml, y a-t-il une limitation du nombre d'appel récursif comme dans la plupart des langages impératifs ?

    Absolument aucune limitation si les appels récursifs sont terminaux : il se font alors en espace constant sur la pile. Voir ma réponse du dessous à gndl.

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

  • [^] # 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.

  • [^] # Re: Lisibilité

    Posté par  . En réponse au journal Portage de TapTempo en OCaml. Évalué à 5. Dernière modification le 13 mars 2018 à 17:49.

    Comme tu es plus habitué au paradigme impératif, je donne ci-dessous la boucle while équivalente à mon code avec une fonction récursive, puis je donne une explication pour passer de l'un à l'autre.

    (*
      valeurs impératives modifiées pendant la boucle,
      on les définit à l'éxtérieur avant de rentrer dedans
    *)
    let samples = Sample.create sample_size in
    let stamp = ref (Mtime_clock.now ()) in
    let key_pressed = ref (try input_char stdin with End_of_file -> 'q') in
    
    (* on rentre dans la boucle *)
    while !key_pressed <> 'q' do
      (* on ne fait quelque chose qu'en cas d'appui sur 'enter' *)
      if !key_pressed = '\n' then begin
        let new_stamp = Mtime_clock.now () in
        let elapsed = Mtime.(Span.to_s (span !stamp new_stamp)) in
    
        (* logique de mise à jour de la variable `samples` *)
        if elapsed > reset_delay then Sample.reset samples;
        Sample.add new_stamp samples;
    
        (* on affiche le message adapté *)
        show_bpm samples;
    
        (* mise à jour de la variable `stamp` *)
        stamp := new_stamp;
      end;
      (* mise à jour de la variable `key_pressed` *)
      key_pressed := try input_char stdin with End_of_file -> 'q'
    done;

    Le principe d'une boucle while, comme illustré dans cet exemple, est d'être une procédure qui modifie par effet de bords certaines variables qui lui sont globales (ici essentiellement samples et stamp) tant qu'une condition est satisfaite (ici, tant que l'on n'a pas appuyé sur q ou que l'entrée standard est n'est pas au bout). En tant que telle, du point de vue d'OCaml, c'est une expression qui a une valeur (comme n'importe quelle expression du langage) de type unit : ce type ne contient qu'une seule valeur (on parle de type singleton) notée (), c'est la valeur qui ne contient aucune information.

    Pour transformer cette boucle en une fonction récursive, il faut d'abord qu'elle retourne la même valeur, à savoir (), lorsqu'elle termine. Ensuite, les variables globales sur laquelle opérait la boucle sont transformées en paramètres de la fonction : elles ne seront plus globales à la boucle, mais locales. On commence donc par écrire :

    let rec loop stamp samples =

    Ensuite, il faut exprimer la condition d'arrêt de la boucle. Celle-ci dépend du caractère que l'on lit sur l'entrée standard, d'où :

    let rec loop stamp samples = match input_char stdin with

    On fait alors une étude de cas, à la manière d'un switch, en commençant par dire quand la fonction termine (et qui renvoie alors ()) :

    let rec loop stamp samples = match input_char stdin with
    (* cas de fin de boucle *)
    | exception End_of_file
    | 'q' -> ()

    Vient ensuite le cœur de la logique de la boucle, ce qui se passe quand on a appuyé sur enter :

    let rec loop stamp samples = match input_char stdin with
    (* cas de fin de boucle *)
    | exception End_of_file
    | 'q' -> ()
    (* on a pressé 'enter' *)
    | '\n' ->
      (* on remet la même logique qu'avec la boucle while *)
      let new_stamp = Mtime_clock.now () in
      let elapsed = Mtime.(Span.to_s (span !stamp new_stamp)) in
    
      (* logique de mise à jour de la variable `samples` *)
      if elapsed > reset_delay then Sample.reset samples;
      Sample.add new_stamp samples;
    
      (* on affiche le message adapté *)
      show_bpm samples;
    
      (* ici on ne met pas à jour la variable stamp mais
         on relance la boucle avec les nouveux paramètres *)
      loop new_stamp samples

    Il reste enfin à traiter le cas où l'on a appuyé sur une autre touche : on ne fait rien et on relance la boucle avec les mêmes paramètres

    let rec loop stamp samples = match input_char stdin with
    (* je ne réécris pas la gestion des autres cas *)
    
    (* cas par défaut : on boucle sans rien changer *)
    | _ -> loop stamp samples

    Maintenant que le corps de la fonction est écrit, il ne reste plus qu'à l'appeler pour lancer la boucle. Pour ce faire, on appelle la fonction avec, pour paramètres, les valeurs initiales des variables globales de la boucle while :

    let rec loop stamp samples = match input_char stdin with
    (* 
      bla bla
      bla bla
    *)
    in loop (Mtime_clock.now ()) (Sample.create sample_size)

    Voilà le principe général pour transformer une boucle while en fonction récursive : on transforme les variables globales de la boucle en variables locales, et à chaque tour on lui passe les nouvelles valeurs.

    Ceci étant, ce genre d'approche n'est pas propre au paradigme fonctionnel, mais ce dernier en fait un usage omniprésent et c'est la route vers la pureté (absence d'effets de bords)1. Pour l'instant, la variable samples fonctionne toujours pas effet de bords et, dans le message précédent, j'ai juste modifié l'écriture de sa logique de mise à jour en utilisant une approche par pipe avec le code :

    samples
    |- (if elapsed > reset_delay then Sample.reset else ignore)
    |- Sample.add new_stamp
    |- show_bpm
    |> loop new_stamp

    Mais si, à la place d'une structure impérative, j'utilisais une structure purement applicative, j'aurais juste à changer les deux premiers combinateurs de tuyaux : un pipe |> au lieu d'un T |- et utiliser la fonction identity au lieu de ignore

    samples
    |> (if elapsed > reset_delay then Sample.reset else identity)
    |> Sample.add new_stamp
    |- show_bpm (* ici on log donc on utilise toujours le tee *)
    |> loop new_stamp

    Derrière cette vision par pipeline, il y a une notion élémentaire de mathématique (bon c'est pas du niveau primaire, mais début de lycée ;-), à savoir la composition de fonction.

    composition

    que l'on peut écrire (la pipeline est assez visible sur le diagramme) :

    fun x -> x |> f |> g |> h

    L'opérateur de composition est une opérateur d'ordre supérieur : il prend deux fonctions en entrée et en renvoie une en sortie; raison pour laquelle il a une place centrale de le paradigme de la programmation fonctionnelle. Que peux-t-on faire avec une fonction ?

    • les utiliser, ça on le fait dans tous les langages de la même façon ;
    • les composer, ça c'est plus simple à écrire dans un langage fonctionnel.

    J'espère que ces explications t'aideront un peu mieux à comprendre certains principes élémentaires à la base du paradigme fonctionnel.


    1. En réalité, c'est cette recherche d'absence d'effets de bord qui nous les fait écrire ainsi. On obtient alors des fonctions récursives avec appel dits terminaux, que le compilateur optimisera comme une simple boucle (il fera la traduction dans le sens inverse de celle que je viens de faire). Voir mon commentaire sur la version de taptempo en Emacs Lisp. 

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

  • [^] # Re: Intéressant mais ....

    Posté par  . En réponse au journal upt: l'outil parfait pour empaqueter TapTempo. Évalué à 4. Dernière modification le 13 mars 2018 à 17:34.

    Comme les compilateurs qui se respectent et qui se compilent eux-mêmes, n'importe quelle version d'upt pourra packager toutes les autres y compris elle-même. :-D

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

  • [^] # Re: Ou pas

    Posté par  . En réponse au journal TapTempo en PHP. Évalué à 5. Dernière modification le 13 mars 2018 à 10:46.

    Cela étant tu devrais peut être faire un appel à stty icanon avant de quitter ton programme, pour remettre le terminal dans sa configuration initiale.

    Pour l'option echo, tu peux jouer avec cette commande dans ton terminal :

    $ stty -echo

    Attention : après on ne voit plus à l'écran ce que l'on tape (pratique pour la saisie d'un mot de passe ;-), il faut taper à l'aveugle la commande :

    $ stty echo

    pour remettre les choses en ordre. :-)

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

  • [^] # Re: Bouh !

    Posté par  . En réponse au journal TapTempo en emacs lisp. Évalué à 6. Dernière modification le 13 mars 2018 à 10:38.

    Seconde erreur de ma part : ce n'est pas un appel récursif terminal… Je ne sais pas encore comment écrire ça.

    Avec un accumulateur ;-)
    Je ne suis pas un expert en Elisp, mais ça doit ressembler à cela :

    (defun fact-tailrec (n acc)
      "Factorielle récursive terminale"
      (if (= n 0)
         acc
         (fact-tailrec ((- n 1) (* n acc)))))
    
    (defun factorielle (n)
       fact-tailrec (n 1))

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