J'ai souvent entendu dire que d'autres développeurs utilisent cette expression pour "annoncer" certains modèles ou développer de meilleures pratiques. La plupart du temps, cette expression est utilisée pour parler des avantages de la programmation fonctionnelle.
La phrase "Facile à raisonner à propos de" a été utilisée telle quelle, sans aucune explication ni échantillon de code. Donc, pour moi, cela devient comme le prochain mot "buzz" que les développeurs "plus expérimentés" utilisent dans leurs discussions.
Question: Pouvez-vous donner quelques exemples de "Pas facile de raisonner à propos de", donc on peut comparer avec les exemples "Facile de raisonner à propos de"?
Réponses:
À mon sens, l'expression "facile à raisonner à propos de" se réfère à un code qui est facile à "exécuter dans votre tête".
Quand on regarde un morceau de code, s'il est court, clairement écrit, avec des noms bien et une mutation minimale de valeurs, alors travailler mentalement à travers ce que fait le code est une tâche (relativement) facile.
Un long morceau de code avec des noms médiocres, des variables qui changent constamment de valeur et de ramifications compliquées nécessiteront normalement, par exemple, un stylo et un morceau de papier pour aider à garder une trace de l’état actuel. Un tel code ne peut donc pas être facilement manipulé dans votre tête. Un tel code n’est donc pas facile à raisonner.
la source
"Code easy to reason about" almost exclusively alludes to its mathematical properties and formal verification
- cela ressemble à peu près à une réponse à la question. Vous voudrez peut-être poster cela comme réponse au lieu de ne pas être en désaccord sur la nature de la réponse (subjective) dans les commentaires.Un mécanisme ou un morceau de code est facile à déterminer quand vous devez prendre en compte peu d'éléments pour prédire ce qu'il va faire, et les éléments que vous devez prendre en compte sont facilement disponibles.
Les vraies fonctions sans effets secondaires et sans état sont faciles à raisonner car la sortie est entièrement déterminée par l'entrée, qui se trouve exactement dans les paramètres.
Inversement, il est beaucoup plus difficile de raisonner un objet avec un état, car vous devez tenir compte de l'état dans lequel l'objet se trouve quand une méthode est appelée, ce qui signifie que vous devez penser aux autres situations qui pourraient conduire à la position de l'objet dans un objet. état particulier.
Les variables globales sont encore pires: pour raisonner sur le code qui lit une variable globale, vous devez comprendre où cette variable peut être définie dans votre code et pourquoi - et il peut même ne pas être facile de trouver tous ces emplacements.
La chose la plus difficile à raisonner est la programmation multithread avec un état partagé, car non seulement vous avez un état, mais vous avez plusieurs threads qui le modifient en même temps. Il faut tenir compte de la possibilité qu’à chaque point d’exécution, un autre thread (ou plusieurs d’entre eux!) exécute à peu près n’importe quelle autre partie du code et modifie les données que vous utilisez sous vos yeux. En théorie, cela peut être géré avec des mutexes / des moniteurs / des sections critiques / tout ce que vous appelez, mais en pratique, aucun humain n'est réellement capable de le faire de manière fiable à moins de confiner radicalement l'état partagé et / ou le parallélisme. sections du code.
la source
make
ou même la spécialisation de modèles C ++ et la surcharge de fonctions) peuvent vous remettre dans l’esprit de considérer l’ensemble du programme. Même lorsque vous pensez avoir trouvé la définition de quelque chose, le langage permet à une déclaration plus précise n'importe où dans le programme de la remplacer. Votre IDE pourrait aider avec ceci.sealed
ne pas être la valeur par défaut?Dans le cas de la programmation fonctionnelle, le sens de «Facile à raisonner» est principalement déterministe. J'entendais par là qu'une entrée donnée conduira toujours à la même sortie. Vous pouvez faire ce que vous voulez pour le programme, tant que vous ne touchez pas ce morceau de code, il ne cassera pas.
D'autre part, il est généralement plus difficile de raisonner sur OO car la "sortie" produite dépend de l'état interne de chaque objet impliqué. Cela se traduit généralement par des effets secondaires inattendus : lorsqu’une partie du code est modifiée, une pièce apparemment sans rapport se rompt.
... l'inconvénient de la programmation fonctionnelle est bien sûr qu'en pratique, une grande partie de ce que vous voulez faire est IO et la gestion de l'état.
Cependant, il y a beaucoup d'autres choses qui sont plus difficiles à raisonner, et je conviens avec @Kilian que la simultanéité est un excellent exemple. Les systèmes distribués aussi.
la source
Éviter une discussion plus large et aborder la question spécifique:
Je vous renvoie à "L'histoire de Mel, un vrai programmeur" , un morceau de folklore de programmeur qui date de 1983 et compte donc comme "légende" pour notre profession.
Il raconte l'histoire d'un programmeur écrivant du code privilégiant autant que possible les techniques obscures, y compris du code auto-référentiel et auto-modificateur, et une exploitation délibérée de bogues machine:
Ceci est un exemple de code qui est «difficile à raisonner».
Bien sûr, Mel serait en désaccord ...
la source
Je peux donner un exemple très courant.
Considérez le code C # suivant.
Considérons maintenant cette alternative.
Dans le deuxième exemple, je sais exactement ce que ce code fait en un coup d’œil. Quand je vois
Select
, je sais qu'une liste d'éléments est en train d'être convertie en une liste d'autre chose. Quand je voisWhere
, je sais que certains éléments sont filtrés. En un coup d'œil, je peux comprendre ce quinames
est et l'utiliser efficacement.Quand je vois une
for
boucle, je n'ai aucune idée de ce qui se passe jusqu'à ce que je lise le code. Et parfois, je dois y faire un suivi pour être sûr d'avoir pris en compte tous les effets secondaires. Je dois faire un peu de travail pour arriver à comprendre ce que sont les noms (au-delà de la définition du type) et comment les utiliser efficacement. Ainsi, le premier exemple est plus difficile à raisonner que le second.En fin de compte, être facile à raisonner ici dépend également de la compréhension des méthodes
Select
et des méthodes LINQWhere
. Si vous ne les connaissez pas, le second code est plus difficile à raisonner au départ. Mais vous ne payez que les coûts pour les comprendre une fois. Vous payez le coût pour comprendre unefor
boucle chaque fois que vous en utilisez une et chaque fois qu’elle change. Parfois, le coût en vaut la peine, mais être "plus facile à raisonner" est bien plus important.la source
Une phrase apparentée est (je paraphrase),
Un exemple de relativement "facile à raisonner à propos de" pourrait être RAII .
Un autre exemple pourrait être d'éviter une étreinte mortelle : si vous pouvez garder un verrou et en acquérir un autre, et qu'il y a beaucoup de verrous, il est difficile de vous assurer qu'il n'y a pas de scénario dans lequel une étreinte mortelle pourrait se produire. L'ajout d'une règle du type "il n'y a qu'un seul verrou (global)" ou "vous n'êtes pas autorisé à acquérir un deuxième verrou tant que vous en détenez un premier" rend le système relativement facile à raisonner.
la source
CComPtr<>
avec une fonction de style C (CoUninitialize()
). Je trouve que c'est aussi un exemple bizarre, pour autant que je me souvienne, vous appelez CoInitialize / CoUninitialize au niveau de la portée du module et pour toute la durée de vie du module, par exemple inmain
ou inDllMain
, et non dans une toute petite portée de fonction locale de courte durée, comme indiqué dans l'exemple. .main
fonction de point d'entrée ( ) pour une application. Vous initialisez COM au démarrage, puis vous le désinitialisez juste avant de quitter. Sauf que vous avez des objets globaux, tels que les pointeurs intelligents COM, qui utilisent le paradigme RAII. Concernant les styles de mixage: un objet global qui a initialisé COM dans son ctor et non initialisé dans son dtor est réalisable, et ce que suggère Raymond, mais il est subtil et difficile à raisonner.Le point crucial de la programmation est l'analyse de cas. Alan Perlis a fait remarquer à ce propos dans l’Épigramme 32: Les programmeurs ne doivent pas être mesurés par leur ingéniosité ni par leur logique mais par l’exhaustivité de leur analyse de cas.
Il est facile de raisonner sur une situation si l’analyse de cas est facile. Cela signifie soit qu'il y a peu de cas à prendre en compte, ou, à défaut, quelques cas particuliers : il peut y avoir de grands espaces de cas, mais qui s'effondrent à cause de certaines régularités, ou succombent à une technique de raisonnement telle que l'induction.
Une version récursive d'un algorithme, par exemple, est généralement plus facile à raisonner qu'une version impérative, car elle ne génère pas de cas superflus résultant de la mutation de variables d'état de support n'apparaissant pas dans la version récursive. De plus, la structure de la récursion est telle qu’elle s’inscrit dans un modèle mathématique preuve par induction. Nous n’avons pas à tenir compte de complexités telles que les variantes de boucle et les conditions préalables les plus faibles et rigoureuses.
Un autre aspect de ceci est la structure de l’espace. Il est plus facile de raisonner sur une situation qui présente une division à plat, ou le plus souvent à plat, par rapport à une situation de cas hiérarchique: cas avec sous-cas et sous-sous-cas, etc.
L' orthogonalité est une propriété des systèmes simplifiant le raisonnement : il s'agit de la propriété selon laquelle les cas qui régissent les sous-systèmes restent indépendants lorsque ces sous-systèmes sont combinés. Aucune combinaison ne donne lieu à des "cas spéciaux". Si un élément à quatre cas est combiné à un autre à trois cas orthogonalement, il y en a douze, mais idéalementchaque cas est une combinaison de deux cas qui restent indépendants. En un sens, il n'y a pas vraiment douze cas; les combinaisons ne sont que des "phénomènes émergents" dont nous n'avons pas à nous soucier. Cela signifie que nous avons encore quatre cas auxquels nous pouvons penser sans prendre en compte les trois autres de l'autre sous-système, et vice versa. Si certaines des combinaisons doivent être spécialement identifiées et dotées d'une logique supplémentaire, alors le raisonnement est plus difficile. Dans le pire des cas, chaque combinaison comporte un traitement spécial, puis il y a réellement douze nouveaux cas, qui s'ajoutent aux quatre et trois originaux.
la source
Sûr. Prendre la concurrence:
Sections critiques imposées par les mutex: facile à comprendre car il n'y a qu'un seul principe (deux threads d'exécution ne peuvent pas entrer simultanément dans la section critique), mais sujets à l'inefficacité et à l'impasse.
Modèles alternatifs, par exemple programmation sans verrou ou acteurs: potentiellement beaucoup plus élégant et puissant, mais incroyablement difficile à comprendre, car vous ne pouvez plus vous fier (apparemment) à des concepts fondamentaux tels que "écrivez cette valeur à cet endroit".
Etre facile à raisonner est l’ un des aspects d’une méthode. Mais le choix de la méthode à utiliser nécessite de prendre en compte tous les aspects.
la source
Limitons la tâche au raisonnement formel. Parce que le raisonnement humoristique ou inventif ou poétique a des lois différentes.
Même dans ce cas, l'expression est faiblement définie et ne peut pas être définie de manière stricte. Mais cela ne signifie pas qu'il devrait rester si sombre pour nous. Imaginons qu'une structure passe un test et marque des points différents. Les bons points pour CHAQUE point signifient que la structure est pratique dans tous les aspects et donc "facile à raisonner".
La structure "Facile à raisonner" devrait donner de bonnes notes aux éléments suivants:
Le test est-il subjectif? Oui, naturellement. Mais l'expression elle-même est subjective aussi. Ce qui est facile pour une personne ne l'est pas pour une autre. Les tests doivent donc être différents pour les différents domaines.
la source
L'idée des langages fonctionnels pouvant être raisonnés découle de leur histoire, en particulier ML, qui a été développé comme un langage de programmation analogue aux constructions que la Logic for Computable Functions utilisait pour le raisonnement. La plupart des langages fonctionnels sont plus proches des calculs de programmation formels que des langages impératifs, de sorte que la conversion du code en entrée d'un système de raisonnement est moins onéreuse.
Pour un exemple de système de raisonnement, dans le pi-calcul, chaque emplacement de mémoire modifiable dans un langage impératif doit être représenté sous la forme d'un processus parallèle séparé, tandis qu'une séquence d'opérations fonctionnelles est un processus unique. Quarante ans après le prouveur de théorèmes du CFL, nous travaillons avec des Go de mémoire vive. Avoir des centaines de processus est moins un problème. traite le raisonneur a épuisé l’espace d’état d’environ 3 Go et a corrigé un bogue intermittent. Cela aurait été impossible dans les années 70 ou aurait nécessité un supercalculateur au début des années 90, alors que l'espace d'état d'un programme de langage fonctionnel de taille similaire était suffisamment petit pour permettre de raisonner à l'époque.
D'après les autres réponses, la phrase devient une phrase à la mode, même si la loi de Moore a érodé une grande partie de la difficulté qui empêchait de raisonner au sujet des langues impératives.
la source
Il est facile de raisonner sur un terme spécifique à une culture. C'est pourquoi il est si difficile de trouver des exemples concrets. C'est un terme qui est ancré chez les personnes qui doivent faire le raisonnement.
"Facile à raisonner à propos de" est en fait une expression très auto-descriptive. Si on regarde le code, et veut raisonner ce qu'il fait, c'est facile =)
Ok, le décomposer. Si vous regardez du code, vous voulez généralement qu'il fasse quelque chose. Vous voulez vous assurer qu'il fait ce que vous pensez qu'il devrait faire. Vous développez donc des théories sur ce que le code devrait faire, puis vous le raisonnez pour tenter de démontrer pourquoi le code fonctionne réellement. Vous essayez de penser le code comme un humain (plutôt que comme un ordinateur) et de rationaliser les arguments sur ce que le code peut faire.
Le pire cas pour "facile à raisonner" est lorsque le seul moyen de donner une idée de ce que fait le code est de le parcourir ligne par ligne comme une machine de Turing pour toutes les entrées. Dans ce cas, le seul moyen de raisonner quoi que ce soit sur le code est de vous transformer en un ordinateur et de l'exécuter dans votre tête. Ces pires exemples sont facilement visibles dans les concours de programmation obsolètes, tels que ces 3 lignes de PERL qui déchiffrent RSA:
Quant à facile de raisonner, encore une fois, le terme est très culturel. Vous devez considérer:
Chacun de ceux-ci affecte "facile de raisonner sur" différemment. Prenons comme exemple les compétences du raisonneur. Lorsque j'ai commencé à travailler dans mon entreprise, il était recommandé de développer mes scripts sous MATLAB car il est "facile à raisonner". Pourquoi? Eh bien, tout le monde dans l'entreprise connaissait MATLAB. Si je choisissais une langue différente, il serait difficile pour quiconque de me comprendre. Peu importe que la lisibilité de MATLAB soit atroce pour certaines tâches, tout simplement parce qu’elle n’a pas été conçue pour elles. Plus tard, au fur et à mesure que ma carrière progressait, Python devenait de plus en plus populaire. Soudain, le code MATLAB est devenu "difficile à raisonner" et Python était le langage de prédilection pour écrire du code facile à raisonner.
Pensez également aux idoms que le lecteur peut avoir. Si vous pouvez compter sur votre lecteur pour reconnaître une FFT dans une syntaxe particulière, il est "plus facile de raisonner à propos de" le code si vous vous en tenez à cette syntaxe. Cela leur permet de regarder le fichier texte comme une toile sur laquelle vous avez peint une FFT, plutôt que d'avoir à entrer dans les détails les plus importants. Si vous utilisez C ++, déterminez à quel point vos lecteurs sont à l'aise avec la
std
bibliothèque. Combien aiment-ils la programmation fonctionnelle? Certains des idiomes qui sortent des bibliothèques de conteneurs dépendent beaucoup du style idomatique que vous préférez.Il est également important de comprendre à quelles sortes de questions le lecteur pourrait être intéressé à répondre. Vos lecteurs s'intéressent-ils principalement à la compréhension superficielle du code ou cherchent-ils des bugs au plus profond de leurs entrailles?
Il est intéressant de savoir à quel point le lecteur doit être sûr. Dans de nombreux cas, un raisonnement flou suffit en fait à faire sortir le produit. Dans d'autres cas, tels que le logiciel de vol de la FAA, le lecteur voudra avoir un raisonnement irréprochable. J'ai rencontré un cas dans lequel je plaidais pour l'utilisation de RAII pour une tâche particulière, parce que "vous pouvez simplement le configurer et l'oublier ... cela fera le bon choix". On m'a dit que j'avais tort à ce sujet. Ceux qui voulaient raisonner sur ce code n'étaient pas du genre à "vouloir juste oublier les détails". Pour eux, RAII ressemblait plus à un chad suspendu, les obligeant à réfléchir à tout ce qui peut se produire lorsque vous quittez la portée.
la source