Partage
  • Partager sur Facebook
  • Partager sur Twitter

Pour apprendre correctement la programmation

sur la pedagogie et l'esprit critique

    16 août 2018 à 16:01:29

    Cette discussion suit celle ci : https://openclassrooms.com/forum/sujet/foncteurs-c-pointeur-sur-fonction mais il y a des points a expliquer pour ceux qui veulent apprendre le C++ et la programmation en général (et tout en général).

    Dans la discussion cite, on parle des moyens d'utiliser une fonction comme argument d'une autre fonction. Il existe beaucoup d'approches pour réaliser cela en C++. On a cite les foncteurs de la bibliothèque standard, créer ses foncteurs, utiliser un une lambda, la déduction de types, les template, des pointeurs de fonctions, std::bind, std::function, des design patterns command ou strategy, des classes de polices, etc.

    Cela fait un gros catalogue de syntaxes, assez difficile a assimiler. Surtout si on prend en compte les cas particuliers (fonctions membres, etc).

    Un premier point a comprendre : les devs pros ne connaissent pas toutes les syntaxes par cœur. On s'en fout. On fait comme tout le monde : on regarde la doc. On connait les syntaxes qu'on utilise le plus souvent et on regarde sur google le reste du temps. Les compétences d'un dev pro n'est pas de connaitre pleins de syntaxes !

    Le gros probleme quand on a un gros catalogue de syntaxes comme ça, c'est qu'il va être très difficile de tout retenir et surtout de comprendre les différences subtiles entre chaque solutions. Et on n'est alors pas capable de choisir la syntaxe a utiliser pour résoudre un probleme. L'erreur de Yes Man est de penser que l'esprit critique est suffisant pour faire la part des choses entre les bonnes informations et les mauvaises. Mais quand on n'a pas les informations complètes, comment peut-on avoir une vision critique correct du probleme et des solutions ?

    La compréhension vient de la pratique. C'est parce que vous pratiquerez des syntaxes que vous les comprendrez, que vous comprendrez leurs intérêts et défauts. 

    La bonne approche pour apprendre quelque chose (le C++, la programmation, ou ce que vous voulez), c'est d'apprendre une chose, puis de la mettre en pratique. Et voir les limites d'utiliser de cette chose. Voir que dans un certain contexte, cette solution est limite. Et a ce moment la, vous pouvez apprendre une autre solution, qui répondra a ce nouveau besoin. Et vous saurez quand utiliser la première solution et quand utiliser la seconde solution. Et quand vous atteindrez les limites de solutions, vous pourrez en apprendre une autre, puis une autre.

    Il est préférable de connaitre très bien ou une deux solutions très bien que d'essayer d'en connaitre vaguement 10.

    --------------------------------------------------------------

    On peut alors comparer les approches pédagogiques du C/C++-old-school vs le C++ "moderne".

    En C ou C++ old school, le nombre de syntaxes est limite. Si on peut passer une indirection comme paramètre de fonction, on n'a que le pointeur. si on veut faire de l'allocation dynamique, on a que le pointeur aussi. Si on veut passer une fonction comme paramètre d'une autre fonction, on n'a que le pointeur de fonction. 

    Beaucoup de "vieux" développeurs pensent que le C (ou le C++ old school) est plus simple a apprendre. Et c'est vrai (en partie). Suivant ce que j'ai dit avant, le fait de n'avoir qu'une seule syntaxe permet de se focaliser dessus, de maîtriser correctement cette syntaxe et de bien comprendre les intérêts et défauts de cette syntaxe.

    De ce point de vue la, le C/C++-old-school a un avantage indéniable. (*)

    Le C++ "moderne" ajoute de nombreuses syntaxe pour faire la même chose. Par exemple :

    - pour passer un paramètre de fonction, on peut utiliser une valeur, les pointeurs nus, les références (const ou non, lvalue ou rvalue), les pointeurs intelligents, un std::reference_wrapper, un std::optional. Et on va ajouter la move semantic.

    - pour faire de l'allocation dynamique, on va pouvoir utiliser les pointeurs nus, les pointeurs intelligents, et toutes les classes RAII que propose la bibliothèque standard.

    - pour passer un callable a une fonction, on a vu qu'il y a aussi beaucoup de syntaxes possibles.

    On voit tout de suite le probleme : si on essaie d'apprendre toutes ces syntaxes, l'apprentissage sera beaucoup plus complexe et on aura du mal a choisir quelle syntaxe utiliser selon le contexte. C'est ce que font par exemple des enseignants ou des cours qui ont été formes au C ou C++ old school et qui doivent enseigner le C++ "moderne" sans eux même avoir de recul sur les syntaxes.

    Mais c'est une erreur pédagogique. Si on fait cela, la courbe d'apprentissage est très dure et cela demande beaucoup d'investissement pour apprendre. (**)

    L'approche pédagogique correcte en C++ "moderne" est de continuer un apprentissage progressif. Apprendre une syntaxe et la mettre en pratique. Et apprendre les autres syntaxes quand le besoin se présente. (Et un bon cours doit amener progressivement, via les exercices, a se confronter aux limites des syntaxes connus, ce qui permet ensuite d'introduire les nouvelles syntaxes).

    Plus généralement, il ne faut pas apprendre pour la syntaxe, mais pour répondre a un probleme que l'on veut résoudre.

    Arrivé a ce niveau de la discussion, il y a 2 variantes pédagogiques : soit bottom-top (du bas vers le haut, c'est a dire apprendre en premier les syntaxes bas niveau du C, puis apprendre les nouvelles syntaxes plus haut niveau), soit top-bottom (l'inverse). Personnellement, je pense que la seconde approche est meilleure, mais c'est une autre discussion. (Par contre, contrairement a ce que l'on entend parfois, l'approche top-bottom ne veut pas dire qu'on n'apprend pas les anciennes syntaxes du C++, comme les pointeurs nus ou les pointeurs de fonctions. Cela veut dire qu'on ne les apprend pas dans un premier temps, mais uniquement quand on atteins le niveau pour aborder ce genre de syntaxes).

    ---------------------------------------------------------------------------

    (*) Mais le C présente aussi d'autres difficultés. En particulier, comme le C propose moins de syntaxes, le code va être plus verbeux et il va falloir écrire vous même pleins de fonctionnalités qui sont proposées par le C++ "moderne" (donc plus de travail et plus de risque de faire des erreurs). A noter, cette remarque est valide aussi pour le C++ face a des langages qui proposent des bibliothèques standards plus complètes, comme le Java, Python ou le C#. Pensez a utiliser les bibliothèques externes en C++, pour éviter ce probleme. (Ne réinventez pas la roue).

    (**) Le propos du C++ "moderne" n'est pas réellement d'apprendre a programmer correctement en C++. Même en suivant un cours de C++ old school, vous pouvez apprendre a programmer correctement en C++. La meilleure preuve est que beaucoup de "vieux" experts du C++ qui défendent le C++ "moderne" ont appris via le C et le C++ old school. (C'est mon cas).

    Le propos du C++ "moderne" est surtout d'apprendre efficacement ! C'est a dire ne pas refaire les erreurs d'apprentissage, ne pas devoir désapprendre ces erreurs pour pouvoir progresser, gagner plusieurs mois ou années d'apprentissage du C++. Apres plusieurs années a aider les débutants sur les forums, on voit les erreurs qui reviennent encore et encore et on voit ceux qui arrivent a devenir bon (voire très bon) en C++.

    ---------------------------------------------------------------------------

    Voila mon point de vue sur le probleme de progression pédagogique en C++. Il me semble intéressant que ceux qui débutent comprennent pourquoi on insiste sur certains points, certaines façons d'apprendre. (Et qu'on dit beaucoup de mal de certains cours et auteurs)

    • Partager sur Facebook
    • Partager sur Twitter
      16 août 2018 à 16:29:29

      Salut !

      En effet, je pense que tu as raison quand tu parles de top-bottom VS bottom-top, et que les deux solutions sont acceptables.

      ça me rappelle une question à laquelle il est difficile de répondre à un néophite :

      "Comment marche un ordinateur ?", car on ne sait pas d'ou partir !

      En effet, si tu veux expliquer à quelqu'un comment marche une voiture, tu lui décris d'abord la charrette : c'est plus simple dans tous les sens du terme qu'une voiture, puis ensuite tu complexifie.

      Pour un ordinateur, c'est totalement contre intuitif, car les premiers ordinateurs (les plus "simples") demandaient un niveau de math et de logique impressionnant pour appréhender les premiers codes machine.

      Et ce sont les ordinateurs beaucoup plus complexes et modernes qui permettent à n'importe qui d'écrire du code, mais par dessus une "boîte magique".

      Et c'est souvent très frustrant pour eux, et pour nous quand on tente d'expliquer comment ça marche, on ne sait pas s'il faut commencer par le "top" ou le "bottom"

      • Partager sur Facebook
      • Partager sur Twitter

      Recueil de code C et C++  http://fvirtman.free.fr/recueil/index.html

        16 août 2018 à 16:46:27

        Au sujet de bottom-up/etc Dans l'intro d'un de ces bouquins, et que l'on trouve en ligne (l'intro), Stroustrup expose les diverses approches, et il n'y en n'a pas que 2 selon lui.

        Sur le plan pédagogique, le sujet est intéressant.

        Retrouvé: §0 du PPPuC++: http://www.stroustrup.com/PPP2_Ch0.pdf

        • Partager sur Facebook
        • Partager sur Twitter
        C++: Blog|FAQ C++ dvpz|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS| Bons livres sur le C++| PS: Je ne réponds pas aux questions techniques par MP.
          16 août 2018 à 17:13:30

          gbdivers a écrit:

          Et un bon cours doit amener progressivement, via les exercices, a se confronter aux limites des syntaxes connus, ce qui permet ensuite d'introduire les nouvelles syntaxes.

          Je trouve que ce genre de cours est assez difficile à trouver. (D'ailleurs je ne me rappel pas avoir appris grand chose via un cours. Ce que je sais, c'est à 40% de la lecture sur forum de dév plus expérimentés, et à 60% de l'auto-interprétation à partir de code -une source possible d'erreur donc, on rejoint ce que l'on disait sur l'esprit critique sans avoir vraiment de repères fiables-)

          Et là est tout le problème, en tant que débutant, on se retrouve assez rarement à sortir de notre zone de confort au point de "titiller la limite de la syntaxe".

          Sauf que, lorsqu'on est confronté à des problèmes, on est aussi amenés à faire des recherches, et à tomber sur ces autres syntaxes.

          Au final, on apprend forcément toute ces syntaxes avant d'apprendre leur "réelle valeur ajoutée", m'est avis...

          • Partager sur Facebook
          • Partager sur Twitter
            17 août 2018 à 12:43:22

            Fvirtman a écrit:

            En effet, si tu veux expliquer à quelqu'un comment marche une voiture, tu lui décris d'abord la charrette : c'est plus simple dans tous les sens du terme qu'une voiture, puis ensuite tu complexifie.

            Je vais prendre une autre analogie : tu veux apprendre a conduire une voiture. On commence par t'apprendre a conduire une trotinette, en déplaçant le poids de ton corps a droite ou à gauche pour tourner. A quoi ca va te servir pour apprendre a conduire une voiture avec un volant ?

            C'est une première critique que je ferais sur certaines façons d'utiliser le bottom-top : ce n'est pas parce qu'on explique quelque chose de bas niveau que c'est transposable en haut niveau.

            Fvirtman a écrit:

            Et ce sont les ordinateurs beaucoup plus complexes et modernes qui permettent à n'importe qui d'écrire du code, mais par dessus une "boîte magique".

            J'ai d'autres critiques sur le bottom-top : faire croire que ce n'est pas une boite magique :)

            Premièrement, la notion d'abstraction est fondamental en programmation. La structure des programmes modernes fait que c'est une nécessité de savoir utiliser une boîte noire (le code qu'on utilise) et savoir créer des boîtes noires (notre code qui sera utilisé par d'autres). Une bonne partie de la conception est de répondre à la question : "quelles informations donner à l'utilisateur de mon code pour qu'il puisse l'utiliser correctement, sans avoir besoin d'aller étudier ce qui se passe en interne et sans le perdre dans trop d'informations". C'est le principe meme des interfaces publiques.

            La seconde critique est le fait de "mentir" en donnant les explications sur ce qui se passe dans les boîtes magiques. Quand on va expliquer par exemple ce qui se passe "en interne" quand on crée une variable, en disant que ca va réserver un emplacement dans la mémoire, qu'on va pouvoir écrire dessus en utilisant son adresse, etc. C'est totalement faux dans un ordinateur moderne ! Entre les optimisations faites par le compilateur, la complexité de la hiérarchie de mémoires d'un ordinateur moderne, et la couche d'abstraction faite par le système d'exploitation entre les parties logicielle et matérielle, les explications données sont souvent complètement fausses. Au final, on remplace une boite magique (l'abstraction) par une autre boite magique (l'explication fausse). Quand l'étudiant voudra approfondir ses connaissances, il devra désapprendre ce qu'on lui a appris.

            ------------------------------------------------------

            On en revient a une critique qu'on a déjà fait plusieurs fois : la mise à jour des connaissances des enseignants.

            Si un enseignant donne un cours de programmation embarqué ou un cours de programmation dans une formation d'électronique, l'approche bottom-top en expliquant ce modèle mémoire simple (qui est valide dans certains contextes), alors cela a du sens.

            Si un enseignant utilise cette approche bottom-top dans un cours où les élèves utilisent des ordinateurs modernes (et donc que les explications données sont fausses), alors cela ne va pas.

            Un enseignant qui connaît les différents modèles mémoire, est à jour au niveau de ses connaissances (en particulier sur le pourquoi des fonctionnalités des nouvelles normes du C++, sur les problématiques de conception logiciel moderne, etc) et qui choisit en connaissance de cause d'utiliser l'approche bottom-top, ça ne me pose pas trop de problème. On pourrait en discuter.

            Malheureusement, ce que l'on constate, c'est que les cours qui utilisent l'approche bottom-top ne le font pas parce que leurs auteurs pensent que c'est la meilleure approche. Ils le font parce que c'est comme ça qu'ils ont appris, parce qu'ils n'ont pas mis à jour leurs connaissances, parce qu'ils ne prennent pas ou n'ont pas le temps de mettre à jour leur cours, etc. Ce qui rend ces cours juste mauvais.

            En soi, ce n'est pas l'approche bottom-top qui est criticable. C'est le trop grand nombre de cours qui utilisent cette approche et qui sont mauvais.

            Sillimon a écrit:

            Au final, on apprend forcément toute ces syntaxes avant d'apprendre leur "réelle valeur ajoutée", m'est avis...

            Oui, cela arrive souvent. Mais en pratique, on ne les assimile pas. Quand on sera face a un probleme, on aura retenu 1-2 approches et on n'utilisera pas les autres. Puis, au cours de nos années de pratiques en milieu pro, on va retomber sur ces problématiques et ces solutions. Et à ce moment là, on va enfin les comprendre et les assimiler. Et les réutiliser.

            Faire un bon cours est très difficile. Ca prend beaucoup de temps, c'est compliqué de mettre en pratique une bonne pédagogie, il y a un délai de plusieurs années entre le moment où l'on enseigne quelque chose et le moment où l'on voit les vrais effets dans le milieu pro, etc.

            • Partager sur Facebook
            • Partager sur Twitter
              17 août 2018 à 13:20:11

              Quand on part du haut niveau, on peut s'appuyer sur une "machine notionnelle" (peu de références en français, mais voir à "notional machine").

              Ca consiste à baser ses explications sur une machine _abstraite_ facile à décrire (et simple de préférence). C'est qu'on fait quand on dit que quand on appelle   x = f(2,3);

              int f (int a, int b) {
                 int c;
                 c = a + b;
                 return c;
              }
              

              ça

              • alloue un cadre de pile, avec 3 variables et l'adresse de retour
              • met dans a une copie du paramètre (2),
              • etc.

              C'est complètement faux pour la plupart des implémentations (on utilise des registres, et on ne va rien allouer sur la pile dans ce cas), mais ça suffit pour expliquer.

              Du reste la norme d'un langage explique essentiellement la machine notionnelle qui est derrière. Ce qui se passe vraiment est le problème des implémenteurs, qui doivent réaliser un truc qui a un comportement _observable_ conforme.

              La nuance c'est d'expliquer le comportement en disant bien que ça fonctionne "en gros comme si".

              -
              Edité par michelbillaud 17 août 2018 à 13:59:00

              • Partager sur Facebook
              • Partager sur Twitter
                17 août 2018 à 14:54:00

                Merci, je ne connaissais pas ce concept de "notional machine". J'ai trouvé des choses intéressantes sur le sujet, je vais regarder ça.

                EDIT :

                C’s notional machine is closer to the notional machine of the CPU
                 itself, but is still a step above the CPU itself (e.g., there are no assignment statements 
                or types in assembly language)

                (source : https://computinged.wordpress.com/2012/05/24/defining-what-does-it-mean-to-understand-computing/ )

                C'est très con, je n'avais jamais pensé à ça, mais c'est effectivement vrai : la notion de type n'existe pas vraiment dans un CPU, ça montre bien que le C ne permet pas de comprendre "ce qui se passe dedans".

                -
                Edité par gbdivers 17 août 2018 à 15:17:24

                • Partager sur Facebook
                • Partager sur Twitter

                Pour apprendre correctement la programmation

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
                • Editeur
                • Markdown