Programmation propre lors de l'écriture de code scientifique


169

Je n'écris pas vraiment de grands projets. Je ne maintiens pas une base de données volumineuse ni des millions de lignes de code.

Mon code est principalement du type "script" - des éléments pour tester des fonctions mathématiques ou pour simuler quelque chose - de "programmation scientifique". Les programmes les plus longs sur lesquels j'ai travaillé jusqu'ici comptent quelques centaines de lignes de code et la plupart des programmes sur lesquels je travaille tournent autour de 150.

Mon code est aussi la merde. J'ai réalisé cela l'autre jour alors que j'essayais de trouver un fichier que j'ai écrit il y a quelque temps, mais que j'ai probablement écrasé et que je n'utilise pas le contrôle de version, ce qui fait que beaucoup d'entre vous sont affolés de stupidité.

Le style de mon code est compliqué et est rempli de commentaires obsolètes indiquant des manières alternatives de faire quelque chose ou de lignes de code copiées. Bien que les noms de variables soient toujours très descriptifs au début, au fur et à mesure que j'ajoute ou modifie des éléments, par exemple, quelque chose de nouveau que quelqu'un souhaite tester, le code se superpose et est écrasé. avoir un cadre, je commence à utiliser des noms de variables de merde et le fichier va au pot.

Dans le projet sur lequel je travaille actuellement, je suis dans la phase où tout cela revient à me mordre fort. Mais le problème est (mis à part le contrôle de version, la création d'un nouveau fichier pour chaque nouvelle itération et son enregistrement quelque part dans un fichier texte, ce qui aidera probablement considérablement la situation). Je ne sais pas vraiment comment procéder pour améliorer mon style de codage actuel.

Des tests unitaires sont-ils nécessaires pour écrire de plus petits morceaux de code? Qu'en est-il de la POO? Quelles sortes d'approches sont bonnes pour écrire du code bon et propre rapidement lors de la "programmation scientifique" par opposition au travail sur des projets plus importants?

Je pose ces questions parce que souvent, la programmation elle-même n'est pas très complexe. C'est plus sur les mathématiques ou les sciences que je teste ou que je fais de la recherche avec la programmation. Par exemple, une classe est-elle nécessaire lorsque deux variables et une fonction pourraient probablement s'en occuper? (Pensez que ce sont généralement des situations dans lesquelles la vitesse du programme est préférable pour être plus rapide - lorsque vous exécutez plus de 25 000 000 pas de temps d'une simulation, vous voulez plutôt que ce soit le cas.)

C'est peut-être trop large, et si oui, je m'excuse, mais en regardant les livres de programmation, ils semblent souvent être adressés à des projets plus importants. Mon code n'a pas besoin de la POO, et il est déjà sacrément court, donc ce n'est pas comme "oh, mais le fichier sera réduit de mille lignes si nous faisons cela!" Je veux savoir comment "recommencer" et programmer proprement sur ces projets plus petits et plus rapides.

Je serais heureux de fournir des détails plus spécifiques, mais plus les conseils sont généraux, plus ils me seront utiles. Je programme en Python 3.


Quelqu'un a suggéré un duplicata. Permettez-moi de préciser que je ne parle pas d'ignorer les normes de programmation standard. De toute évidence, il existe une raison pour que ces normes existent. Mais d’autre part, est-il vraiment judicieux d’écrire du code dit OO alors que des tâches standard auraient pu être faites, auraient été beaucoup plus rapides à écrire et auraient eu un niveau de lisibilité similaire en raison de la brièveté de la programme?

Il y a des exceptions. De plus, il existe probablement des normes pour la programmation scientifique qui vont au-delà des simples normes. Je parle de ceux-là aussi. Il ne s’agit pas de savoir si les normes de codage normales doivent être ignorées lors de l’écriture de code scientifique, mais bien d’écrire du code scientifique propre!


Mise à jour

Je pensais juste que j'ajouterais une sorte de mise à jour "pas tout à fait une semaine plus tard". Tous vos conseils ont été extrêmement utiles. J'utilise maintenant le contrôle de version - git, avec git kraken pour une interface graphique. Il est très facile à utiliser et a considérablement nettoyé mes fichiers - plus besoin d’anciens fichiers collés, ni d’anciennes versions de code commentées "au cas où".

J'ai également installé pylint et l'ai exécuté sur tout mon code. Un fichier a obtenu un score négatif au départ; Je ne sais même pas comment cela a été possible. Mon fichier principal a démarré à un score de ~ 1,83 / 10 et se situe maintenant à ~ 9,1 / 10. Tout le code est maintenant assez bien conforme aux normes. Je l'ai aussi parcouru de mes propres yeux en mettant à jour les noms de variables qui avaient disparu ... euh ... de travers, et en cherchant des sections à refactoriser.

En particulier, sur ce site, j’ai posé une question récente sur la refactorisation d’une de mes fonctions principales. C’est maintenant beaucoup plus propre et beaucoup plus court: au lieu d’une fonction longue et gonflée, si remplie, elle est maintenant inférieure à la moitié. la taille et beaucoup plus facile à comprendre ce qui se passe.

Ma prochaine étape est la mise en œuvre de "tests unitaires". J'entends par fichier un fichier que je peux exécuter sur mon fichier principal et qui examine toutes les fonctions qu'il contient avec des instructions assert et try / excepts, ce qui n'est probablement pas la meilleure façon de le faire et qui génère beaucoup de code en double, mais je vais continuer à lire et essayer de trouver comment mieux le faire.

J'ai également considérablement mis à jour la documentation que j'avais déjà écrite et ajouté des fichiers supplémentaires, tels qu'un tableur Excel, la documentation et un document associé dans le référentiel github. Cela ressemble un peu à un vrai projet de programmation maintenant.

Donc ... je suppose que tout cela est pour dire: merci .




8
Si vous souhaitez activement améliorer votre code, vous pouvez envisager d’en publier quelques-unes sur Code Review . La communauté se fera un plaisir de vous aider avec cela.
hoffmale

7
Quand vous dites "en dehors du contrôle de version, créer un nouveau fichier pour chaque nouvelle itération et l'enregistrer dans un fichier texte quelque part" par "et" voulez-vous dire "ou" parce que si vous utilisez le contrôle de version, vous ne devriez ne pas être copier coller des versions. Le fait est que le contrôle de version conserve toute l'ancienne version pour vous
Richard Tingle

2
@mathreadler Je ne pense pas que vous compreniez bien. Ouais, seulement je vais probablement tous lire et jouer avec le code (même si on ne sait jamais, et j'ai quelqu'un avec qui je peux programmer, bien que dans une langue différente) ... mais le code est toujours merde. Je vais devoir le lire plus tard et redécouvrir ce que je fais. Il est un problème, et je peux en témoigner parce que je suis dans les effets maintenant, et les choses sont devenues plus faciles comme je l' ai mis en œuvre le contrôle de version et d' autres techniques proposées ici.
bruyère

Réponses:


163

C'est un problème assez commun pour les scientifiques. Je l'ai souvent vu, et cela tient toujours au fait que la programmation est un outil que vous choisissez sur le côté comme outil pour faire votre travail.

Donc, vos scripts sont un désordre. Je vais aller à l'encontre du bon sens et dire que, si vous programmez seul, ce n'est pas si grave! Vous n'allez plus jamais toucher la majeure partie de ce que vous écrivez, aussi consacrez-vous trop de temps à écrire un joli code au lieu de produire de la «valeur» (le résultat de votre script) ne vous fera donc pas grand chose.

Cependant, il y aura un moment où vous devrez revenir à quelque chose que vous avez fait et voir exactement comment quelque chose fonctionnait. De plus, si d'autres scientifiques doivent revoir votre code, il est primordial qu'il soit aussi clair et concis que possible, afin que tout le monde puisse le comprendre.

Votre problème principal va être la lisibilité, alors voici quelques conseils pour améliorer:

Noms de variables:

Les scientifiques aiment utiliser des notations concises. Toutes les équations mathématiques utilisent généralement des lettres simples comme variables et je ne serais pas surpris de voir de nombreuses variables très courtes dans votre code. Cela nuit beaucoup à la lisibilité. Quand vous reviendrez à votre code, vous ne vous souviendrez plus de ce que représentent y, i et x2, et vous passerez beaucoup de temps à essayer de le comprendre. Essayez plutôt de nommer explicitement vos variables, en utilisant des noms qui représentent exactement ce qu’elles sont.

Divisez votre code en fonctions:

Maintenant que vous avez renommé toutes vos variables, vos équations semblent terribles et comportent plusieurs lignes.

Au lieu de la laisser dans votre programme principal, déplacez cette équation vers une autre fonction et nommez-la en conséquence. Maintenant, au lieu d’avoir une ligne de code énorme et foirée, vous aurez une brève instruction vous indiquant exactement ce qui se passe et quelle équation vous avez utilisée. Cela améliore à la fois votre programme principal, puisque vous n’aurez même pas à regarder l’équation réelle pour savoir ce que vous avez fait, et le code de l’équation lui-même, comme dans une fonction séparée, vous pouvez nommer vos variables comme vous le souhaitez, et revenir à les lettres simples plus familiers.

Dans cet ordre d'idées, essayez de trouver tous les éléments de code qui représentent quelque chose, en particulier si vous devez le faire plusieurs fois dans votre code, et les diviser en fonctions. Vous découvrirez que votre code deviendra rapidement plus facile à lire et que vous pourrez utiliser les mêmes fonctions sans écrire plus de code.

Cerise sur le gâteau, si ces fonctions sont nécessaires dans plusieurs de vos programmes, vous pouvez simplement créer une bibliothèque pour elles, et vous les aurez toujours disponibles.

Variables globales:

Quand j'étais débutant, je pensais que c'était un excellent moyen de transmettre les données dont j'avais besoin à de nombreux moments de mon programme. Il s'avère qu'il y a beaucoup d'autres façons de faire circuler des choses, et la seule chose que les variables globales font est de donner des maux de tête aux gens, car si vous passez à un point aléatoire de votre programme, vous ne saurez jamais quand cette valeur a été utilisée ou modifiée pour la dernière fois. le traquer sera une douleur. Essayez de les éviter autant que possible.

Si vos fonctions doivent renvoyer ou modifier plusieurs valeurs, créez une classe avec ces valeurs et transmettez-les en tant que paramètre ou faites en sorte que la fonction renvoie plusieurs valeurs (avec des n-uplets nommés) et affectez ces valeurs dans le code de l'appelant.

Contrôle de version

Cela n'améliore pas directement la lisibilité, mais vous aide à faire tout ce qui précède. Chaque fois que vous apportez des modifications, engagez-vous dans le contrôle de version (un référentiel Git local suffira), et si quelque chose ne fonctionne pas, regardez ce que vous avez modifié ou annulez! Cela facilitera la refactorisation de votre code et constituera un filet de sécurité si vous cassez accidentellement des choses.

Garder tout cela à l’esprit vous permettra d’écrire du code plus clair et plus efficace, mais vous aidera également à trouver les erreurs possibles plus rapidement, car vous n’aurez pas à parcourir des fonctions gigantesques et des variables compliquées.


56
Excellent conseil. Cependant, je ne peux pas voter pour le commentaire "ce n'est pas si mal". C'est vraiment mauvais. Les scripts scientifiques de faible qualité sont un problème important pour la reproductibilité dans l'analyse des données et une source d'erreur fréquente dans l'analyse. Écrire un bon code est utile non seulement pour pouvoir le comprendre plus tard, mais aussi pour éviter les erreurs.
Jack Aidley

22
Si le code est une implémentation d'une formule bien connue, les noms de variable à une lettre et ainsi de suite pourraient être la bonne chose à faire. Cela dépend du public ... et plus précisément, on attend du lecteur qu'il sache déjà ce que les noms signifient.
cHao

11
@cHao, le problème n'est pas lorsque ces variables sont insérées dans la formule (d'où le conseil "les renommer dans la fonction"), mais lorsqu'elles sont lues et manipulées en dehors de celle-ci, et lorsqu'elles entrent en conflit avec d'autres variables plus tard (Par exemple, j'ai vu des personnes ayant besoin de trois variables "x" et les
nommant

4
"Je vais aller contre le sens commun ..." Non, vous ne l'êtes pas. Vous allez à l'encontre du dogme dominant, qui va à l'encontre du sens commun. ;) Ce sont tous des conseils parfaitement sains.
jpmc26

3
En tant que (ancien) programmeur scientifique, j’adopte généralement une règle de trois. Si je finis par écrire trois fois un code similaire, la fonctionnalité est développée et écrite dans un module séparé avec documentation (souvent des commentaires, mais cela suffit). Cela limite le chaos de la programmation ad hoc et me permet de construire une bibliothèque sur laquelle je pourrai me développer à l'avenir.
rcollyer

141

Physicien ici. Été là.

Je dirais que votre problème ne concerne pas le choix des outils ni les paradigmes de programmation (tests unitaires, POO, peu importe). C'est à propos de l' attitude , de la mentalité. Le fait que vos noms de variables soient bien choisis au début et finissent par être de la merde est assez révélateur. Si vous pensez que votre code est «exécuté une fois, puis jeté», alors ce sera inévitablement un gâchis. Si vous le considérez comme le produit de l'artisanat et de l'amour, ce sera magnifique.

Je crois qu'il n'y a qu'une seule recette pour écrire du code propre: écrivez-le pour l'être humain qui va le lire, pas pour l'interprète qui va le faire fonctionner. L'interprète ne se soucie pas si votre code est un gâchis, mais le lecteur humain ne garde.

Vous êtes un scientifique. Vous pouvez probablement passer beaucoup de temps à peaufiner un article scientifique. Si votre premier brouillon semble compliqué, vous le refactoriserez jusqu'à ce que la logique coule de la manière la plus naturelle qui soit. Vous voulez que vos collègues le lisent et trouvent les arguments parfaitement clairs. Vous voulez que vos étudiants soient en mesure d'apprendre de cela.

L'écriture de code propre est exactement la même chose. Pensez à votre code comme une explication détaillée d'un algorithme qui ne se trouve qu'incidemment être lisible par machine. Imaginez que vous allez le publier sous forme d'article que les gens liront. Vous allez même le montrer lors d'une conférence et guider le public ligne par ligne. Maintenant, répétez votre présentation . Oui, ligne par ligne ! Embarrassant, n'est-ce pas? Alors nettoyez vos diapositives (euh ... je veux dire, votre code), et répétez à nouveau. Répétez jusqu'à ce que vous soyez satisfait du résultat.

Ce serait encore mieux si, après les répétitions, vous pouviez montrer votre code à de vraies personnes plutôt qu'à de simples personnes imaginaires et à votre futur. Le parcourir ligne par ligne s'appelle une «promenade de code», et ce n'est pas une pratique idiote.

Bien sûr, tout cela a un coût. L'écriture de code propre prend beaucoup plus de temps que l'écriture de code jetable. Vous seul pouvez évaluer si les avantages dépassent les coûts pour votre cas d'utilisation particulier.

En ce qui concerne les outils, j'ai déjà dit qu'ils n'étaient pas si importants. Cependant, si je devais en choisir un, je dirais que le contrôle de version est le plus utile.


32
«Écrire du code propre, c'est exactement la même chose [écrire un article clair].» J'approuve totalement cela, bien dit!
juandesant

43
C'est quelque chose que la plupart des programmeurs professionnels oublient de dire parce que c'est tellement évident. Votre code est terminé lorsqu'il est lisible et modifiable par un autre programmeur. Pas quand il fonctionne et produit la sortie correcte. L'opérateur doit passer une heure supplémentaire par script à refactoriser et commenter son code pour le rendre lisible par l'homme.
UEFI

31
Bien que l'écriture de code propre prenne beaucoup plus de temps que celle d'un code jetable, il est bien plus important que la lecture d'un code jetable prenne beaucoup plus de temps que la lecture d'un code propre.
user949300

3
@UEFI Non, c'est quelque chose que la plupart des programmeurs professionnels ne réalisent même pas. Ou s'en fout.
jpmc26

2
D'accord à 100%. Statisticien devenu programmeur, je fais donc pas mal de programmation "scientifique" au travail. Un code clair, avec des commentaires significatifs, est une bouée de sauvetage lorsque vous devez revenir à ce code 1, 4 ou 12 mois plus tard. La lecture du code vous indique ce qu’il fait. La lecture des commentaires vous indique ce que le code est censé faire.
railsdog

82

Le contrôle de version vous donnera probablement le maximum pour votre argent. Ce n'est pas seulement pour le stockage à long terme, c'est idéal pour suivre vos expériences à court terme et revenir à la dernière version qui a fonctionné, en gardant des notes tout au long.

Viennent ensuite les tests unitaires. La chose à propos des tests unitaires est que même les bases de code avec des millions de lignes de code sont testées un par une. Les tests unitaires sont effectués dans le plus petit, au plus bas niveau d'abstraction. Cela signifie qu'il n'y a fondamentalement aucune différence entre les tests unitaires écrits pour les bases de code de petite taille et ceux utilisés pour les grandes. Il y en a juste plus.

Les tests unitaires sont le meilleur moyen d'éviter de casser quelque chose qui fonctionnait déjà lorsque vous corrigez quelque chose, ou au moins de vous dire rapidement lorsque vous corrigez. Ils sont en réalité plus utiles lorsque vous êtes aussi peu habile en tant que programmeur, ou que vous ne savez pas ou ne voulez pas écrire du code plus détaillé qui est structuré pour rendre les erreurs moins probables ou plus évidentes.

Entre le contrôle de version et l'écriture de tests unitaires au fur et à mesure, votre code deviendra naturellement beaucoup plus propre. D'autres techniques de codage plus propre peuvent être apprises lorsque vous atteignez un plateau.


78
Je teste religieusement les unités de mon code, mais j’ai trouvé qu’un code exploratoire et scientifique était moins qu’inutile . La méthodologie ne semble fondamentalement pas fonctionner ici. Je ne connais aucun scientifique en informatique de mon domaine qui teste par unité leur code d'analyse. Je ne suis pas sûr de la raison de cette inadéquation, mais l' une des raisons est certainement que, sauf pour les unités triviales, il est difficile ou impossible d'établir de bons tests.
Konrad Rudolph

22
@KonradRudolph l'astuce dans ces cas est susceptible d'être une séparation nette des problèmes entre les parties de votre code qui ont un comportement clairement définissable (lisez cette entrée, calculez cette valeur) des parties de votre code qui sont véritablement exploratoires ou qui s'adaptent par exemple. une sortie ou une visualisation lisible par l'homme. Le problème ici est probablement que la mauvaise séparation des préoccupations conduit à brouiller ces lignes, ce qui laisse penser que les tests unitaires sont impossibles dans ce contexte, ce qui vous ramène au début dans un cycle de répétition.
Ant P

18
Par ailleurs, le contrôle de version fonctionne également très bien pour les documents LaTeX, car le format permet de différer les textes. De cette manière, vous pouvez avoir un référentiel à la fois pour vos papiers et le code qui les supporte. Je suggère de regarder dans le contrôle de version distribuée, comme Git. La courbe d'apprentissage est un peu longue, mais une fois que vous le comprenez, vous disposez d'un moyen propre et agréable d'itérer votre développement et vous avez quelques options intéressantes pour utiliser une plate-forme comme Github, qui propose des comptes d'équipe gratuits pour les universitaires .
Dan Bryant

12
@AntP Il est possible qu'il n'y ait pas beaucoup de code pouvant être refactorisé de manière significative en unités testables bien définies. Une grande partie du code scientifique consiste essentiellement à enregistrer un ensemble de bibliothèques. Ces bibliothèques seront déjà bien testées et bien structurées, ce qui signifie que l'auteur n'a plus qu'à écrire "colle", et d'après mon expérience, il est presque impossible d'écrire des tests unitaires pour une colle non tautologique.
James_pic

7
"Entre le contrôle de version et l'écriture de tests unitaires, votre code deviendra naturellement beaucoup plus propre." Ce n'est pas vrai Je peux en témoigner personnellement. Aucun de ces outils ne vous empêche d'écrire du code de merde, et en particulier, l'écriture de tests de merde sur du code de merde rend le nettoyage encore plus difficile. Les tests ne sont pas une solution miracle, et parler comme ils sont est une chose terrible à faire pour tout développeur qui apprend encore (qui est tout le monde). Le contrôle de version ne provoque généralement jamais de dommages au code lui-même, contrairement aux mauvais tests.
jpmc26

29

(à part l'utilisation du contrôle de version, la création d'un nouveau fichier pour chaque nouvelle itération et son enregistrement dans un fichier texte quelque part, ce qui aidera probablement considérablement la situation)

Vous auriez probablement compris cela vous-même, mais si vous devez " enregistrer tout cela dans un fichier texte quelque part ", vous n'utilisez pas tout le potentiel du système de contrôle de version. Utilisez quelque chose comme Subversion, git ou Mercurial et écrivez un bon message de commit avec chaque commit et vous aurez un journal qui sert à la fonction du fichier texte mais ne peut pas être séparé du référentiel.

Cela dit, utiliser le contrôle de version est la chose la plus importante que vous puissiez faire pour une raison qu’aucune des réponses existantes ne mentionne: la reproductibilité des résultats . Si vous pouvez soit utiliser vos messages de journal, soit ajouter une note aux résultats avec le numéro de révision, vous pouvez être sûr de pouvoir régénérer les résultats et vous serez mieux placé pour publier le code avec le document.

Des tests unitaires sont-ils nécessaires pour écrire de plus petits morceaux de code? Qu'en est-il de la POO? Quelles sortes d'approches sont bonnes pour écrire du code bon et propre rapidement lors de la "programmation scientifique" par opposition au travail sur des projets plus importants?

Les tests unitaires ne sont jamais nécessaires, mais ils sont utiles si (a) le code est suffisamment modulaire pour que vous puissiez tester les unités plutôt que le tout; (b) vous pouvez créer des tests. Idéalement, vous seriez capable d'écrire manuellement le résultat attendu plutôt que de le générer avec le code, bien que le générer à l'aide de code puisse au moins vous donner des tests de régression vous indiquant si quelque chose a changé son comportement. Déterminez simplement si les tests sont plus susceptibles d’être bogués que le code qu’ils testent.

OOP est un outil. Utilisez-le si cela vous aide, mais ce n'est pas le seul paradigme. Je suppose que vous ne connaissez vraiment que la programmation procédurale: si tel est le cas, dans le contexte décrit, il serait plus avantageux d'étudier la programmation fonctionnelle que la programmation orientée objet, et en particulier la nécessité d'éviter les effets secondaires autant que possible. Python peut être écrit dans un style très fonctionnel.


4
+1 pour les messages de validation; ce sont comme des commentaires qui ne peuvent pas être obsolètes parce qu'ils sont liés à la version du code quand ils étaient réellement applicables. Pour comprendre votre ancien code, il est plus facile de parcourir l'historique du projet (si les modifications sont validées avec une granularité raisonnable) que de lire des commentaires obsolètes.
Silly Freak

Subversion, git et Mercurial ne sont pas fongibles. Je recommanderais fortement d'utiliser Git (ou Mercurial) avec un référentiel local plutôt que Subversion. Avec un codeur solo, les défauts de Subversion sont moins problématiques, mais ce n’est pas un excellent outil de développement collaboratif et cela pourrait éventuellement se produire en recherche
mcottle

2
@mcottle, je préfère personnellement git mais je ne pensais pas que c'était le bon endroit pour entrer dans les détails sur les différences, d'autant plus que le choix est l'une des guerres de religion actives. Mieux vaut encourager OP à utiliser quelque chose que de lui faire peur, et la décision n’est en aucun cas permanente.
Peter Taylor

21

En troisième cycle, j’ai moi-même écrit un code exigeant beaucoup d’algorithmes. C'est un peu difficile à casser. Pour le dire grossièrement, de nombreuses conventions de programmation sont construites autour de l'idée de placer des informations dans une base de données, de les récupérer au bon moment, puis de masser ces données pour les présenter à un utilisateur, en utilisant généralement une bibliothèque pour tous les calculs. algorithmes lourds parties de ce processus. Pour ces programmes, tout ce que vous avez entendu sur la programmation orientée objet, diviser le code en petites fonctions et tout rendre compréhensible en un coup d'œil, si possible, constitue un excellent conseil. Mais cela ne fonctionne pas tout à fait pour un code exigeant beaucoup d’algorithmes, ou un code qui implémente des calculs mathématiques complexes et peu d’autres choses.

Si vous écrivez des scripts pour effectuer des calculs scientifiques, vous avez probablement des papiers avec les équations ou les algorithmes que vous utilisez écrits. Si vous utilisez vous-même de nouvelles idées que vous avez découvertes, nous espérons les publier dans vos propres journaux. Dans ce cas, la règle est la suivante: vous voulez que votre code se lit autant que possible comme les équations publiées. Voici une réponse sur Software Engineering.SE avec plus de 200 votes positifs préconisant cette approche et expliquant à quoi elle ressemble: Existe - t-il une excuse pour les noms de variable courts?

Autre exemple, Simbody , un outil de simulation de physique utilisé pour la recherche et l’ingénierie en physique , contient d’ excellents extraits de code . Ces extraits contiennent un commentaire indiquant une équation utilisée pour un calcul, suivi d'un code aussi proche que possible des équations implémentées.

ContactGeometry.cpp:

// t = (-b +/- sqrt(b^2-4ac)) / 2a
// Discriminant must be nonnegative for real surfaces
// but could be slightly negative due to numerical noise.
Real sqrtd = std::sqrt(std::max(B*B - 4*A*C, Real(0)));
Vec2 t = Vec2(sqrtd - B, -sqrtd - B) / (2*A);

ContactGeometry_Sphere.cpp:

// Solve the scalar Jacobi equation
//
//        j''(s) + K(s)*j(s) = 0 ,                                     (1)
//
// where K is the Gaussian curvature and (.)' := d(.)/ds denotes differentiation
// with respect to the arc length s. Then, j is the directional sensitivity and
// we obtain the corresponding variational vector field by multiplying b*j. For
// a sphere, K = R^(-2) and the solution of equation (1) becomes
//
//        j  = R * sin(1/R * s)                                        (2)
//          j' =     cos(1/R * s) ,                                      (3)
//
// where equation (2) is the standard solution of a non-damped oscillator. Its
// period is 2*pi*R and its amplitude is R.

// Forward directional sensitivity from P to Q
Vec2 jPQ(R*sin(k * s), cos(k * s));
geod.addDirectionalSensitivityPtoQ(jPQ);

// Backwards directional sensitivity from Q to P
Vec2 jQP(R*sin(k * (L-s)), cos(k * (L-s)));
geod.addDirectionalSensitivityQtoP(jQP);

9
Plus un pour faire son "code pour lire autant que possible les équations publiées". Désolé, les partisans de noms de variables longs et significatifs. Les noms les plus significatifs du code scientifique sont souvent méchants, courts et brutaux, précisément parce que c'est exactement la convention utilisée dans un article de journal scientifique que le code tente de mettre en œuvre. Pour une partie de code lourde en équations qui implémente les équations trouvées dans un article de journal, il est souvent préférable de rester aussi proche que possible de la nomenclature dans l'article, et si cela va à l'encontre des bonnes normes de codage, tenace.
David Hammen

@ David Hammen: En tant qu'étudiant diplômé, je respecte cela. En tant que programmeur, j’insisterais alors pour que vous ayez un bloc de commentaires géant en haut de chaque fonction décrivant en anglais simple (ou dans la langue de votre choix) ce que chaque variable représentait, même s’il ne s’agissait que d’un espace réservé temporaire. De cette façon, j'ai au moins une référence à regarder en arrière.
tonysdg

1
@DavidHammen En outre, le support de Python pour UTF-8 dans les fichiers source et des règles simples pour les noms de variables, il est facile de déclarer λou au φlieu du laid lambda_ou phy...
Mathias Ettinger

1
@tonysdg Vous avez déjà une référence; ça s'appelle "Hammen, et al. (2018)" (ou autre chose). Il expliquera la signification des variables de manière beaucoup plus détaillée que n'importe quel bloc de commentaires. La raison pour laquelle les noms de variable sont proches de la notation dans le papier est précisément pour faciliter la connexion entre ce qui est dans le papier et ce qui est dans le code.
Personne

17

Mon travail consiste donc à publier et à préserver des données de recherche pour le système de l'Université de Californie. Quelques personnes ont parlé de reproductibilité, et je pense que c'est là le problème fondamental: documenter votre code de la même manière que tout ce qui est nécessaire pour reproduire votre expérience et, idéalement, écrire un code qui le rendrait simple pour quelqu'un d'autre pour reproduire votre expérience et pour vérifier vos résultats pour les sources d'erreur.

Mais quelque chose que je n'ai pas vu mentionné, et que j'estime important, est que les agences de financement considèrent de plus en plus la publication de logiciels comme une partie de la publication de données et considèrent la publication de logiciel comme une exigence de la science ouverte.

À cette fin, si vous voulez quelque chose de spécifique, qui cible les chercheurs plutôt que les développeurs de logiciels en général, je ne saurais trop recommander l’ organisation Software Carpentry . Si vous pouvez assister à l'un de leurs ateliers , c'est parfait. si tout ce que vous avez le temps / l'accès à faire est de lire certains de leurs articles sur les meilleures pratiques en informatique scientifique , c'est bien aussi. De ce dernier:

Les scientifiques développent généralement leur propre logiciel à ces fins, car cela nécessite une connaissance approfondie du domaine. En conséquence, des études récentes ont montré que les scientifiques consacrent généralement 30% ou plus de leur temps à développer des logiciels. Cependant, 90% ou plus d'entre eux sont principalement autodidactes et ne sont donc pas familiarisés avec les pratiques de développement logiciel de base telles que l'écriture de code maintenable, l'utilisation du suivi de versions et des suiveurs de problèmes, la révision de codes, les tests unitaires et l'automatisation des tâches.

Nous pensons que les logiciels ne sont qu’un autre type d’appareil expérimental et doivent être construits, vérifiés et utilisés avec autant de soin que tout appareil physique. Cependant, alors que la plupart des scientifiques veillent à valider leurs équipements de laboratoire et de terrain, la plupart ne savent pas à quel point leurs logiciels sont fiables. Cela peut entraîner de graves erreurs ayant une incidence sur les conclusions centrales des recherches publiées. …

De plus, les logiciels étant souvent utilisés pour plusieurs projets et réutilisés par d'autres scientifiques, les erreurs de calcul peuvent avoir des effets disproportionnés sur le processus scientifique. Ce type d'impact en cascade a provoqué plusieurs retraits importants lorsqu'une erreur du code d'un autre groupe n'a été découverte qu'après la publication.

Un aperçu de haut niveau des pratiques qu'ils recommandent:

  1. Écrire des programmes pour les gens, pas les ordinateurs
  2. Laisse l'ordinateur faire le travail
  3. Faire des changements incrémentiels
  4. Ne te répète pas (ni les autres)
  5. Prévoyez des erreurs
  6. Optimiser le logiciel seulement après qu'il fonctionne correctement
  7. Document design et but, pas mécanique
  8. Collaborer

Le document donne des détails considérables sur chacun de ces points.


16

Est-il vraiment logique d'écrire du code dit OOP alors que des tâches standard auraient pu être réalisées, auraient été beaucoup plus rapides à écrire et auraient eu un niveau de lisibilité similaire en raison de la brièveté du programme?

Réponse personnelle:
je fais aussi beaucoup de scripts à des fins scientifiques. Pour les scripts plus petits, j'essaie simplement de suivre les bonnes pratiques de programmation générales (c'est-à-dire utiliser le contrôle de version, pratiquer l'auto-contrôle avec des noms de variable). Si je suis en train d'écrire quelque chose pour ouvrir ou visualiser rapidement un jeu de données, je ne me soucie pas de la programmation orientée objet.

Réponse générale:
"Cela dépend." Mais si vous avez du mal à déterminer quand utiliser un concept de programmation ou des paradigmes, voici quelques points à prendre en compte:

  • Évolutivité: le script va-t-il rester autonome ou sera-t-il éventuellement utilisé dans un programme plus grand? Si tel est le cas, la plus grande programmation utilise-t-elle la POO? Le code de votre script peut-il être facilement intégré au programme plus vaste?
  • Modularité: En général, votre code devrait être modulaire. Cependant, OOP divise le code en morceaux de manière très spéciale. Ce type de modularité (c'est-à-dire la scission de votre script en classes) a-t-il un sens pour ce que vous faites?

Je veux savoir comment "recommencer" et programmer proprement sur ces projets plus petits et plus rapides.

N ° 1: Familiarisez-vous avec ce qui existe:
Même si vous "ne" créez qu'un script (et que vous vous souciez vraiment de la composante scientifique), vous devriez prendre le temps de vous familiariser avec différents concepts et paradigmes de programmation. De cette façon, vous pouvez avoir une meilleure idée de ce que vous ne devriez pas ou ne voulez pas utiliser et quand. Cela peut sembler un peu intimidant. Et vous pouvez toujours avoir la question, "Où dois-je commencer / qu'est-ce que je commence à regarder?" J'essaie d'expliquer un bon point de départ dans les deux points suivants.

N ° 2: Commencez à réparer ce que vous savez être faux:
Personnellement, je commencerais par les choses que je sais être mauvaises. Obtenez un certain contrôle de version et commencez à vous discipliner pour vous améliorer avec ces noms de variables (c'est un combat sérieux). Réparer ce que vous savez être faux peut sembler évident. Cependant, d'après mon expérience, j'ai constaté que régler un problème m'amène à autre chose, et ainsi de suite. Avant que je ne le sache, j’ai dévoilé 10 choses que j’avais mal faites et compris comment les réparer ou les mettre en œuvre de manière propre.

N ° 3: Trouvez un partenaire de programmation:
si vous ne souhaitez pas recommencer à zéro, pensez à faire équipe avec un développeur et demandez-lui de revoir votre code. Même s'ils ne comprennent pas la partie scientifique de ce que vous faites, ils pourraient peut-être vous dire ce que vous auriez pu faire pour rendre votre code plus élégant.

No 4: Recherchez des consortiums:
je ne sais pas dans quel domaine scientifique vous êtes. Mais, en fonction de ce que vous faites dans le monde scientifique, essayez de rechercher des consortiums, des groupes de travail ou des participants à une conférence. Ensuite, voyez s’il existe des normes sur lesquelles ils travaillent. Cela peut vous conduire à certaines normes de codage. Par exemple, je fais beaucoup de travail géospatial. L'examen des documents de la conférence et des groupes de travail m'a conduit au Open Geospatial Consortium . Ils travaillent notamment sur les normes de développement géospatial.

J'espère que ça aide!


Note latérale: Je sais que vous venez d'utiliser la programmation orientée objet comme exemple. Je ne voulais pas que vous pensiez que je me suis retrouvé coincé sur la façon de gérer l'écriture de code en utilisant la POO. Il était simplement plus facile d'écrire une réponse en continuant avec cet exemple.


Je pense que le n ° 3 est le problème le plus important - un programmeur expérimenté peut expliquer au PO les concepts dont il a besoin (n ° 1), comment organiser les scripts de manière plus efficace et comment utiliser le contrôle de version (n ° 2).
Doc Brown

16

Je recommanderais de s'en tenir au principe d'Unix: Keep It Simple, Stupid! (BAISER)

Autrement dit: faites une chose à la fois et faites-le bien.

Qu'est-ce que ça veut dire? Tout d’abord, cela signifie que vos fonctions doivent être courtes. Toute fonction dont le but, l’utilisation et la mise en œuvre ne peuvent pas être entièrement compris en quelques secondes est définitivement trop longue. Il est probable que vous fassiez plusieurs choses à la fois, chacune d’elles devant être une fonction qui leur est propre. Alors divisez-le.

En termes de lignes de code, mon heuristique est que 10 lignes sont une bonne fonction et que toute valeur supérieure à 20 est probablement une merde. D'autres personnes ont d'autres heuristiques. L'important est de limiter la durée à quelque chose que vous pouvez saisir en un instant.

Comment divisez-vous une longue fonction? Eh bien, vous devez d’abord rechercher des modèles de code répétés. Ensuite, vous factorisez ces modèles de code, leur attribuez un nom descriptif et observez votre code se rétrécir . En réalité, le meilleur refactoring est le refactoring qui réduit la taille du code.

Cela est particulièrement vrai lorsque la fonction en question a été programmée avec copier-coller. Chaque fois que vous voyez un tel motif répété, vous savez immédiatement que cela devrait probablement être transformé en une fonction qui lui est propre. C'est le principe de ne pas se répéter (DRY) . Chaque fois que vous frappez un copier-coller, vous faites quelque chose de mal! Créez une fonction à la place.

Une anecdote Une
fois, j'ai passé plusieurs mois à refactoriser du code comportant environ 500 lignes. Après avoir terminé, le code total était environ mille lignes plus courtes. J'avais produit une sortie négative en termes de lignes de code. Je devais la compagnie ( http://www.geekherocomic.com/2008/10/09/programmers-salary-policy/index.html ). Pourtant, je crois fermement que cela a été l'une de mes œuvres les plus précieuses que j'ai jamais réalisées ...

Certaines fonctions peuvent être longues car elles effectuent plusieurs choses distinctes les unes après les autres. Ce ne sont pas des violations DRY, mais ils peuvent également être divisés. Le résultat est souvent une fonction de haut niveau qui appelle une multitude de fonctions qui implémentent les étapes individuelles des fonctions d'origine. Cela augmentera généralement la taille du code, mais les noms de fonction ajoutés fonctionneront à merveille pour rendre le code plus lisible. Parce que maintenant vous avez une fonction de niveau supérieur avec toutes ses étapes explicitement nommées. De plus, après cette scission, il est clairement indiqué quelle étape agit sur quelles données. (Arguments de fonction. Vous n'utilisez pas de variables globales, n'est-ce pas?)

Une bonne heuristique pour ce type de division de fonction de section est à chaque fois que vous êtes tenté d'écrire un commentaire de section ou lorsque vous trouvez un commentaire de section dans votre code. C’est très probablement l’un des points où votre fonction devrait être scindée. Le commentaire de section peut également servir à donner un nom à la nouvelle fonction.

Les principes KISS et DRY peuvent vous mener loin. Vous n'avez pas besoin de commencer immédiatement avec la programmation orientée objet, etc., vous pouvez souvent réaliser de grandes simplifications en appliquant simplement ces deux méthodes. Cependant, il est rentable à long terme de connaître la POO et d’autres paradigmes, car ils vous fournissent des outils supplémentaires que vous pouvez utiliser pour rendre votre code de programme plus clair.

Enfin, enregistrez chaque action avec un commit. Vous intégrez quelque chose dans une nouvelle fonction, c'est un commit . Vous fusionnez deux fonctions en une seule, car elles font vraiment la même chose, c’est un commit . Si vous renommez une variable, c'est un commit . S'engager fréquemment. Si un jour se passe et que vous ne vous êtes pas engagé, vous avez probablement commis une erreur.


2
Grands points sur la division des méthodes longues. Une autre bonne heuristique concernant le premier paragraphe après l'anecdote: si votre méthode peut être divisée logiquement en sections et que vous êtes tenté d'écrire un commentaire expliquant ce que fait chaque section, vous devez la séparer aux commentaires. Bonne nouvelle, ces commentaires vous donnent probablement une bonne idée de la manière d'appeler les nouvelles méthodes.
Jaquez

@ Jaquez Ah, totalement oublié celui-là. Merci de me le rappeler. J'ai mis à jour ma réponse pour inclure ceci :-)
cmaster

1
Excellents points, je voudrais simplifier les choses en disant que "DRY" est le facteur le plus important. Identifier les "répétitions" et les supprimer est la pierre angulaire de presque toutes les autres constructions de programmation. En d'autres termes, toutes les constructions de programmation sont là, au moins en partie, pour vous aider à créer du code DRY. Commencez par dire "Pas de duplication à jamais" puis exercez-vous à l'identifier et à l'éliminer. Soyez très ouvert sur ce qui pourrait être un doublon - même si ce n'est pas un code similaire, il pourrait s'agir d'une fonctionnalité de duplication ...
Bill K

11

Je suis d'accord avec les autres pour dire que le contrôle de version résoudra immédiatement bon nombre de vos problèmes. Plus précisément:

  • Il n'est pas nécessaire de conserver une liste des modifications apportées, d'avoir plusieurs copies d'un fichier, etc., car c'est ce que le contrôle de version prend en charge.
  • Pas plus de fichiers perdus à cause d'écrasements, etc.
  • Pas besoin de commentaires obsolètes, de code mort, etc., conservés "au cas où"; une fois qu'ils sont engagés dans le contrôle de version, n'hésitez pas à les utiliser. Cela peut être très libérateur!

Je dirais qu'il ne faut pas trop y penser: utilisez simplement git. Tenez-vous en à des commandes simples (par exemple, une seule masterbranche), utilisez peut-être une interface graphique, et tout devrait bien se passer. En bonus, vous pouvez utiliser gitlab, github, etc. pour des publications et des sauvegardes gratuites;)

La raison pour laquelle j'ai écrit cette réponse était pour aborder deux choses que vous pourriez essayer et que je n'ai pas vues mentionnées ci-dessus. La première consiste à utiliser les assertions comme une alternative légère au test unitaire. Les tests unitaires ont tendance à rester "en dehors" de la fonction / du module / quel que soit le test: ils envoient généralement des données à une fonction, reçoivent un résultat, puis vérifient certaines propriétés de ce résultat. Ceci est généralement une bonne idée, mais pourrait être gênant (surtout pour le code "jetable") pour plusieurs raisons:

  • Les tests unitaires doivent décider quelles données ils vont donner à la fonction. Ces données doivent être réalistes (sinon il ne sert à rien de les tester), elles doivent avoir le format correct, etc.
  • Les tests unitaires doivent avoir "accès" aux choses qu'ils veulent affirmer. En particulier, les tests unitaires ne peuvent vérifier aucune des données intermédiaires dans une fonction; nous devions briser cette fonction en de plus petites pièces, tester ces pièces et les brancher ensemble ailleurs.
  • Les tests unitaires sont également supposés être pertinents pour le programme. Par exemple, les suites de tests peuvent devenir "obsolètes" si d'importants changements ont été apportés depuis leur dernière exécution, et il pourrait même y avoir un grand nombre de tests pour du code qui n'est même plus utilisé.

Les assertions ne présentent pas ces inconvénients, car elles sont vérifiées lors de l'exécution normale d'un programme. En particulier:

  • Comme ils sont exécutés dans le cadre de l'exécution normale du programme, nous disposons de données réelles dans le monde réel. Cela ne nécessite pas de curation séparée et (par définition) est réaliste et a le format correct.
  • Les assertions peuvent être écrites n'importe où dans le code afin que nous puissions les placer partout où nous avons accès aux données que nous voulons vérifier. Si nous voulons tester une valeur intermédiaire dans une fonction, nous pouvons simplement mettre quelques assertions au milieu de cette fonction!
  • Comme elles sont écrites en ligne, les assertions ne peuvent pas être "désynchronisées" avec la structure du code. Si nous nous assurons que les assertions sont vérifiées par défaut, nous n'avons pas à nous inquiéter du fait qu'elles deviennent "périmées", car nous verrons immédiatement si elles passent ou non à la prochaine exécution du programme!

Vous mentionnez la vitesse en tant que facteur, auquel cas la vérification d'assertion peut être indésirable dans cette boucle (mais reste utile pour vérifier la configuration et le traitement ultérieur). Cependant, presque toutes les implémentations d'assertions fournissent un moyen de les désactiver; Par exemple, en Python, ils peuvent apparemment être désactivés en utilisant l' -Ooption (je ne le savais pas, car je n'avais jamais ressenti le besoin de désactiver aucune de mes assertions auparavant). Je vous recommande de les laisser surpar défaut; Si votre cycle de codage / débogage / test ralentit, il vaudrait peut-être mieux tester avec un sous-ensemble plus petit de vos données ou effectuer moins d'itérations d'une simulation lors des tests ou autre. Si vous ne finissent par désactiver les assertions dans les essais non-tests pour des raisons de performance, la première chose que je vous recommande de faire est la mesure si elles sont en fait la source du ralentissement! (Il est très facile de se leurrer en ce qui concerne les goulots d'étranglement des performances)

Mon dernier conseil serait d'utiliser un système de compilation qui gère vos dépendances. Personnellement, j'utilise Nix pour cela, mais j'ai également entendu de bonnes choses sur Guix . Il existe également des alternatives telles que Docker, qui sont beaucoup moins utiles d'un point de vue scientifique mais peut-être un peu plus familières.

Des systèmes tels que Nix ne sont que récemment (un peu) devenus populaires, et certains pourraient penser qu’ils sont excessifs pour du code "jetable" comme vous le décrivez, mais leur avantage pour la reproductibilité du calcul scientifique est énorme. Considérons un script shell pour exécuter un test, comme ceci (par exemple run.sh):

#!/usr/bin/env bash
set -e
make all
./analyse < ./dataset > output.csv

Nous pouvons le réécrire dans une "dérivation" Nix à la place, comme ceci (par exemple run.nix):

with import <nixpkgs> {};
runCommand "output.csv" {} ''
  cp -a ${./.} src
  cd src
  make all
  ./analyse < ./dataset > $out
''

La substance entre ''...''est le code bash, identique à celui que nous avions auparavant, sauf que vous ${...}pouvez l'utiliser pour "épisser" le contenu d'autres chaînes (dans ce cas ./., le chemin sera étendu au chemin du répertoire qui le contient run.nix). La with import ...ligne importe la bibliothèque standard de Nix , qui permet runCommandd’exécuter du code bash. Nous pouvons exécuter notre expérience en utilisant nix-build run.nix, ce qui donnera un chemin comme /nix/store/1wv437qdjg6j171gjanj5fvg5kxc828p-output.csv.

Alors qu'est-ce que cela nous achète? Nix établira automatiquement un environnement «propre», qui n’a accès qu’à ce que nous avons explicitement demandé. En particulier, il n’a pas accès aux variables telles $HOMEque les logiciels système que nous avons installés. Cela rend le résultat indépendant des détails de notre machine actuelle, comme le contenu ~/.configou les versions des programmes que nous avons installés; AKA, ce qui empêche d'autres personnes de reproduire nos résultats! C'est pourquoi j'ai ajouté quecpcommande, car le projet ne sera pas accessible par défaut. Cela peut paraître ennuyeux que le logiciel du système ne soit pas disponible pour un script Nix, mais il en va tout autrement: nous n'avons besoin de rien installé sur notre système (autre que Nix) pour l'utiliser dans un script; nous le demandons simplement et Nix ira chercher / compiler / ce qui est nécessaire (la plupart des choses seront téléchargées sous forme de fichiers binaires; la bibliothèque standard est également énorme!). Par exemple, si nous voulons un ensemble de paquets Python et Haskell particuliers, pour certaines versions de ces langues, ainsi que d'autres objets indésirables (car pourquoi pas?):

with import <nixpkgs> {};
runCommand "output.csv"
  {
    buildInputs = [
      gcc49 libjson zlib
      haskell.packages.ghc802.pandoc
      (python34.withPackages (pyPkgs: [
        pyPkgs.beautifulsoup4 pyPkgs.numpy pyPkgs.scipy
        pyPkgs.tensorflowWithoutCuda
      ]))
    ];
  }
  ''
    cp -a ${./.} src
    cd src
    make all
    ./analyse < ./dataset > $out
  ''

Le même nix-build run.nixexécutera ceci, récupérant tout ce que nous avons demandé en premier (et le mettant en cache au cas où nous le voudrions plus tard). La sortie (tout fichier / répertoire appelé $out) sera stockée par Nix, qui est le chemin qu’elle crache. Elle est identifiée par le hachage cryptographique de toutes les entrées demandées (contenu du script, autres packages, noms, indicateurs de compilation, etc.); ces autres paquets sont identifiés par des hachages de leurs entrées, et ainsi de suite, de telle sorte que nous ayons une chaîne complète de provinence pour tout, depuis la version de GCC qui compilait la version de GCC qui compilait bash, et ainsi de suite!

J'espère que j'ai montré que cela nous achetait beaucoup pour du code scientifique et qu'il était relativement facile de commencer avec. Il commence également à être pris très au sérieux par les scientifiques, par exemple (top hit Google) https://dl.acm.org/citation.cfm?id=2830172 pourrait donc être une compétence précieuse à cultiver (tout comme la programmation)


2
Réponse utile très détaillée - J'aime beaucoup les autres réponses, mais les affirmations semblent être une première étape très utile.
bruyère

9

Sans passer à l'état d'esprit total de contrôle de version + packaging + tests unitaires (qui sont de bonnes pratiques de programmation que vous devriez essayer de mettre en place à un moment donné), une solution intermédiaire qui conviendrait à mon avis est d'utiliser Jupiter Notebook . Cela semble mieux s'intégrer au calcul scientifique.

Il a l’avantage de pouvoir mélanger vos pensées avec le code; expliquer pourquoi une approche est meilleure qu'une autre et laisser l'ancien code tel quel dans une section ad-hoc. De plus, bien utiliser les cellules vous mènera naturellement à fragmenter votre code et à l’organiser en fonctions qui peuvent aider à sa compréhension.


1
De plus, cela aide vraiment à la reproductibilité - vous pouvez exécuter exactement le même code pour générer un chiffre de publication, par exemple, ou pour revenir à quelque chose que vous avez mis de côté il y a des mois, peut-être pour incorporer des commentaires de relecteur.
afaulconbridge

Pour ceux qui veulent en savoir plus, on parle également de programmation alphabète.
llrs

6

Les principales réponses sont déjà bonnes, mais je voulais aborder certaines de vos questions directement.

Des tests unitaires sont-ils nécessaires pour écrire de plus petits morceaux de code?

La taille du code n'est pas directement liée à la nécessité de tests unitaires. Cela est lié indirectement: les tests unitaires ont plus de valeur dans les bases de code complexes , et les petites bases de code ne sont généralement pas aussi complexes que les plus grandes.

Les tests unitaires brillent pour le code où il est facile de faire des erreurs, ou lorsque vous allez avoir plusieurs implémentations de ce code. Les tests unitaires ne vous aident pas beaucoup avec le développement actuel , mais ils vous empêchent également de faire des erreurs qui pourraient causer un mauvais comportement du code existant (même si vous n'avez pas touché à cela).

Supposons que vous avez une application dans laquelle la bibliothèque A effectue la mise en carré des nombres et la bibliothèque B applique le théorème de Pythagore. De toute évidence, B dépend de A. Vous devez corriger quelque chose dans la bibliothèque A et imaginons que vous introduisez un bogue qui divise les nombres en cubes au lieu de les redimensionner.

La bibliothèque B commencera soudainement à se comporter de manière incorrecte, générant éventuellement des exceptions ou donnant simplement une sortie erronée. Et quand vous regardez l'historique des versions de la bibliothèque B, vous voyez qu'il n'est pas touché. Le résultat final problématique est que vous n'avez aucune indication de ce qui pourrait mal se passer et que vous allez devoir déboguer le comportement de B avant de réaliser que le problème est en A. C'est un effort inutile.

Entrez les tests unitaires. Ces tests confirment que la bibliothèque A fonctionne comme prévu. Si vous introduisez un bogue dans la bibliothèque A qui lui renvoie des résultats erronés, vos tests unitaires le détecteront. Par conséquent, vous ne serez pas obligé d'essayer de déboguer la bibliothèque B.
Cela dépasse votre portée, mais dans un développement en intégration continue, des tests unitaires sont exécutés chaque fois que quelqu'un valide du code, ce qui signifie que vous saurez que vous avez cassé quelque chose dès que possible.

Les tests unitaires peuvent être une bénédiction, en particulier pour les opérations mathématiques complexes. Vous effectuez quelques exemples de calculs, puis vous écrivez des tests unitaires qui comparent votre sortie calculée et votre sortie réelle (sur la base des mêmes paramètres d'entrée).

Cependant, notez que les tests unitaires ne vous aideront pas à créer un bon code, mais le maintiendront . Si vous écrivez habituellement du code une fois et ne le revisitez jamais, les tests unitaires seront moins bénéfiques.

Qu'en est-il de la POO?

La POO est une façon de penser à des entités distinctes, par exemple:

Quand un Customerveut acheter un Product, il parle à le Vendorrecevoir un Order. Le Accountantsera ensuite payer le Vendor.

Comparez cela à la façon dont un programmeur fonctionnel réfléchit:

Lorsqu'un client le souhaite purchaseProduct(), il talktoVendor()le souhaite sendOrder()pour lui. Le comptable le fera alors payVendor().

Pommes et oranges. Ni l'un ni l'autre n'est objectivement meilleur que l'autre. Une chose intéressante à noter est que pour la programmation orientée objet, il Vendorest mentionné deux fois, mais il fait référence à la même chose. Cependant, pour la programmation fonctionnelle, talktoVendor()et payVendor()sont deux choses distinctes.
Cela montre la différence entre les approches. S'il y a beaucoup de logique partagée spécifique au fournisseur entre ces deux actions, alors la POO aide à réduire la duplication de code. Cependant, s'il n'y a pas de logique partagée entre les deux, alors les fusionner en un seul Vendorest un travail inutile (et par conséquent, une programmation fonctionnelle est plus efficace).

Plus souvent qu'autrement, les calculs mathématiques et scientifiques sont des opérations distinctes qui ne reposent pas sur une logique / formule partagée implicite. Pour cette raison, la programmation fonctionnelle est plus souvent utilisée que la POO.

Quelles sortes d'approches sont bonnes pour écrire du code bon et propre rapidement lors de la "programmation scientifique" par opposition au travail sur des projets plus importants?

Votre question implique que la définition de "code propre et bon" change si vous effectuez une programmation scientifique ou travaillez sur des projets plus importants (je suppose que vous voulez dire d'entreprise).

La définition du bon code ne change pas. La nécessité d'éviter la complexité (ce qui peut être fait en écrivant du code propre) change toutefois.

Le même argument revient ici.

  • Si vous ne revisitez jamais l'ancien code et ne comprenez pas parfaitement la logique sans avoir à le compartimenter, ne faites pas d'effort excessif pour rendre les choses maintenables.
  • Si vous revisitez l'ancien code ou si la logique requise est trop complexe pour que vous puissiez la traiter en une fois (ce qui vous oblige donc à compartimenter les solutions), concentrez-vous sur une écriture propre, réutilisable.

Je pose ces questions parce que souvent, la programmation elle-même n'est pas très complexe. C'est plus sur les mathématiques ou les sciences que je teste ou que je fais de la recherche avec la programmation.

Je comprends la distinction que vous faites ici, mais quand vous regardez en arrière le code existant, vous regardez à la fois le calcul et la programmation. Si l' un ou l' autre est artificiel ou complexe, vous aurez du mal à le lire.

Par exemple, une classe est-elle nécessaire lorsque deux variables et une fonction pourraient probablement s'en occuper?

Mis à part les principes de la programmation orientée objet, la principale raison pour laquelle j'écris des classes pour héberger quelques valeurs de données est qu'elle simplifie la déclaration des paramètres de méthode et des valeurs renvoyées. Par exemple, si de nombreuses méthodes utilisent un emplacement (paire lat / lon), je me lasserai vite de devoir taper float latitude, float longitudeet préférerai de beaucoup écrire Location loc.

Cela est d'autant plus complexe si vous considérez que les méthodes renvoient généralement une valeur (à moins que des fonctionnalités spécifiques au langage ne renvoient plus de valeurs), et qu'un emplacement, par exemple, voudrait que vous renvoyiez deux valeurs (lat + lon). Cela vous incite à créer une Locationclasse pour simplifier votre code.

Par exemple, une classe est-elle nécessaire lorsque deux variables et une fonction pourraient probablement s'en occuper?

Une autre chose intéressante à noter est que vous pouvez utiliser la POO sans mélanger les valeurs de données et les méthodes. Tous les développeurs ne sont pas d’accord ici (certains l’appellent un antipattern), mais vous pouvez avoir des modèles de données anémiques dans lesquels vous avez des classes de données distinctes (champs de valeur stockés) et des classes logiques (méthodes de stockage).
Ceci est, bien sûr, sur un spectre. Vous n'avez pas besoin d'être parfaitement anémique, vous pouvez l'utiliser quand vous le jugez à propos.

Par exemple, une méthode qui concatène simplement le prénom et le nom d'une personne peut toujours être hébergée dans la Personclasse elle-même, car il ne s'agit pas vraiment de "logique", mais plutôt d'une valeur calculée.

(Pensez que ce sont généralement des situations dans lesquelles la vitesse du programme est préférable pour être plus rapide - lorsque vous exécutez plus de 25 000 000 pas de temps d'une simulation, vous voulez plutôt que ce soit le cas.)

Une classe est toujours aussi grande que la somme de ses champs. Reprenons l’exemple de Location, qui consiste en deux floatvaleurs, il est important de noter ici qu’un seul Locationobjet occupe autant de mémoire que deux floatvaleurs distinctes .

En ce sens, peu importe que vous utilisiez la POO ou non. L'empreinte mémoire est la même.

La performance en elle-même n’est pas un gros obstacle à franchir. La différence entre, par exemple, l'utilisation d'une méthode globale ou d'une méthode de classe n'a rien à voir avec les performances d'exécution, elle a tout à voir avec la génération de bytecode au moment de la compilation.

Pensez-y de cette façon: que j'écrive ma recette de gâteau en anglais ou en espagnol, cela ne change rien au fait qu'il faudra 30 minutes pour que le gâteau soit cuit (= performance d'exécution). La seule chose que la langue de la recette change est la façon dont le cuisinier mélange les ingrédients (= compilation du code binaire).

Pour Python en particulier, il n'est pas nécessaire de pré-compiler explicitement le code avant de l'appeler. Cependant, lorsque vous ne pré-compilez pas, la compilation aura lieu lors de la tentative d'exécution du code. Quand je dis "runtime", je parle de l'exécution elle-même, pas de la compilation qui pourrait précéder l'exécution.


6

Avantages du code scientifique propre

  • ... en regardant les livres de programmation, ils semblent souvent être abordés dans des projets plus importants.

  • ... est-il vraiment logique d'écrire du code dit OOP alors que des tâches standard auraient pu être faites, auraient été beaucoup plus rapides à écrire et auraient eu un niveau de lisibilité similaire en raison de la brièveté du programme?

Il peut être utile d’examiner votre code du point de vue d’un futur codeur.

  • Pourquoi ont-ils ouvert ce fichier?
  • Que cherchent-ils?

Selon mon expérience,

Un code propre devrait faciliter la vérification de vos résultats

  • Facilitez la tâche aux utilisateurs pour qu'ils sachent exactement ce qu'ils doivent faire pour exécuter votre programme.
  • Vous voudrez peut-être diviser votre programme afin que chaque algorithme puisse être analysé séparément.

  • Évitez d’écrire des fonctions avec des effets secondaires contre-intuitifs dans lesquels une opération non liée entraîne le comportement d’une autre opération. Si vous ne pouvez pas l'éviter, documentez ce dont votre code a besoin et comment le configurer.

Un code propre peut servir d'exemple de code pour les futurs codeurs

Des commentaires clairs (y compris ceux qui montrent comment les fonctions doivent être appelées) et des fonctions bien séparées peuvent faire une différence énorme en ce qui concerne le temps qu'il faut à quelqu'un qui commence (ou qui vous suit) pour tirer quelque chose de votre travail utile.

De plus, créer une véritable "API" pour votre algorithme peut vous aider à mieux vous préparer si vous décidez de transformer vos scripts en une véritable bibliothèque pouvant être utilisée par quelqu'un d'autre.

Recommandations

"Cite" les formules mathématiques en utilisant des commentaires.

  • Ajoutez des commentaires pour "citer" des formules mathématiques, en particulier si vous utilisiez des optimisations (identités trigonométriques, séries de Taylor, etc.).
  • Si vous avez obtenu la formule du livre, ajoutez un commentaire en indiquant que John Smith Method from Some Book 1st Ed. Section 1.2.3 Pg 180, si vous avez trouvé la formule sur un site Web ou dans un document, citez-la également.
  • Je vous recommande d' éviter « lien » seulement des commentaires, assurez - vous que vous faites référence à la méthode par nom quelque part pour permettre aux gens de le google, j'ai couru dans certains commentaires « seul lien » que redirigés vers les anciennes pages internes et ils peuvent être très frustrant .
  • Vous pouvez essayer de taper la formule dans votre commentaire s'il est encore facile de lire en Unicode / ASCII, mais cela peut devenir très gênant (les commentaires de code ne sont pas LaTeX).

Utilisez les commentaires à bon escient

Si vous pouvez améliorer la lisibilité de votre code en utilisant de bons noms de variables / noms de fonctions, faites-le d'abord. N'oubliez pas que les commentaires resteront indéfiniment jusqu'à ce que vous les supprimiez. Essayez donc de faire des commentaires qui ne seront pas périmés.

Utilisez des noms de variables descriptives

  • Les variables à lettre unique peuvent être la meilleure option si elles font partie d'une formule.
  • Il peut être crucial pour les futurs lecteurs de pouvoir regarder le code que vous avez écrit et le comparer à l’équation que vous implémentez.
  • Le cas échéant, envisagez d’ajouter un suffixe pour décrire son sens réel, par exemple ,. xBar_AverageVelocity
  • Comme mentionné précédemment, je vous recommande d'indiquer clairement la formule / méthode que vous utilisez nominalement dans un commentaire quelque part.

Ecrivez du code pour exécuter votre programme contre les bonnes et les mauvaises données connues.

Des tests unitaires sont-ils nécessaires pour écrire de plus petits morceaux de code?

Je pense que les tests unitaires peuvent être utiles. Je pense que la meilleure forme de test unitaire pour le code scientifique est une série de tests qui fonctionnent sur des données connues ou négatives.

Ecrivez du code pour exécuter votre algorithme et vérifiez si le résultat diffère de ce que vous attendiez. Cela vous aidera à trouver des problèmes (potentiellement très difficiles et difficiles à trouver) dans lesquels vous codez accidentellement quelque chose qui cause un résultat faux positif ou faites une erreur qui fait que la fonction renvoie toujours la même valeur.

Notez que cela peut être fait à n'importe quel niveau d'abstraction. Par exemple, vous pouvez tester un algorithme de correspondance de motif complet ou une fonction qui calcule simplement la distance entre deux résultats dans votre processus d'optimisation. Commencez par les zones les plus cruciales pour vos résultats, et / ou les parties du code qui vous préoccupent le plus.

Ajoutez facilement de nouveaux cas de test, envisagez d’ajouter des fonctions "auxiliaires" et structurez efficacement vos données d’entrée. Cela peut signifier éventuellement la sauvegarde des données d'entrée dans un fichier afin que vous puissiez facilement réexécuter des tests, tout en étant très prudent pour éviter les faux positifs ou les cas de test biaisés / résolus de manière triviale.

Pensez à utiliser quelque chose comme la validation croisée , voir cet article sur la validation croisée pour plus d'informations.

Utiliser le contrôle de version

Je recommanderais d'utiliser le contrôle de version et d'héberger votre référentiel sur un site externe. Il existe des sites qui hébergeront des dépôts gratuitement.

Avantages:

  1. Il fournit une sauvegarde en cas de défaillance de votre disque dur
  2. Il fournit un historique qui vous évite de vous inquiéter si un problème récent qui a été soulevé a été causé par la modification accidentelle d'un fichier, parmi d'autres avantages.
  3. Cela vous permet d'utiliser des branches, ce qui est un bon moyen de travailler sur du code expérimental à long terme sans affecter le travail non lié.

Soyez prudent lorsque vous copiez / collez du code

Le style de mon code est compliqué et est rempli de commentaires obsolètes indiquant des manières alternatives de faire quelque chose ou de lignes de code copiées.

  • Copier / coller du code peut vous faire gagner du temps, mais c'est l'une des choses les plus dangereuses que vous puissiez faire, surtout si c'est du code que vous n'avez pas écrit vous-même (par exemple, s'il s'agit du code d'un collègue).

  • Dès que le code est fonctionnel et testé, je vous recommande de le lire très attentivement pour renommer les variables ou commenter tout ce que vous ne comprenez pas.



6

Les outils du métier sont généralement inventés pour répondre à un besoin. Si vous avez le besoin d'utiliser l'outil, sinon, vous n'êtes probablement pas obligé de le faire.

Plus précisément, les programmes scientifiques ne sont pas la cible finale, mais le moyen. Vous écrivez le programme pour résoudre un problème que vous avez maintenant - vous ne vous attendez pas à ce que ce programme soit utilisé par d'autres (et qu'il soit maintenu) dans dix ans. Cela seul signifie que vous n'avez pas besoin de penser aux outils qui permettent au développeur actuel d'enregistrer l'historique d'autres utilisateurs tels que le contrôle de version, ou de capturer des fonctionnalités dans du code comme des tests unitaires.

Qu'est-ce qui vous profiterait alors?

  • Le contrôle de version est agréable car il vous permet de sauvegarder très facilement votre travail. Depuis 2018, github est un endroit très populaire pour le faire (et vous pouvez toujours le déplacer plus tard si nécessaire - git est très flexible). Les procédures de sauvegarde automatique de votre système d'exploitation (Time Machine pour Mac, rsync pour Linux, etc.) constituent un substitut simple et peu coûteux aux sauvegardes. Votre code doit être à plusieurs endroits!
  • Les tests unitaires sont bien, car si vous les écrivez d' abord, vous devez réfléchir à la manière de vérifier ce que fait réellement le code, ce qui vous aide à concevoir une API plus utile pour votre code. Ceci est utile si vous écrivez du code et que vous modifiez un algorithme, car vous savez qu'il fonctionne dans ces cas.
  • Documentation. Apprenez à rédiger la documentation appropriée dans le langage de programmation que vous utilisez (par exemple, javadoc for Java). Ecrivez pour l'avenir vous. Dans ce processus, vous constaterez que de bons noms de variables facilitent la documentation. Répéter. Accordez la même attention à votre documentation qu’un poète aux poèmes.
  • Utilisez de bons outils. Trouvez un IDE qui vous aide et apprenez-le bien. Le refactoring comme renommer les variables en un meilleur nom est beaucoup plus facile de cette façon.
  • Si vous avez des pairs, envisagez d'utiliser l'évaluation par les pairs. Faire examiner et comprendre votre code par un tiers est la version du futur pour laquelle vous écrivez. Si votre pair ne comprend pas votre code, vous ne le ferez probablement pas plus tard.

Comment cette réponse n'a-t-elle pas reçu un vote positif? Il a maintenant. Notre groupe a estimé que la revue par les pairs était l’un des outils les plus efficaces, bien plus important que les tests unitaires en matière de code scientifique. Il est facile de se tromper en traduisant en code un ensemble complexe d’équations d’un article de journal scientifique. Les scientifiques et les ingénieurs font souvent des programmeurs extrêmement pauvres; l'examen par les pairs peut détecter les laideurs architecturales qui rendent le code difficile à maintenir / comprendre / utiliser.
David Hammen

5

En plus des bons conseils déjà donnés, vous voudrez peut-être examiner le but de votre programmation et, par conséquent, ce qui est important pour vous.

"Ce sont les mathématiques ou les sciences que je teste ou que je recherche avec la programmation."

Si le but est d'expérimenter et de tester quelque chose pour votre propre compréhension et que vous sachiez quels devraient être les résultats, votre code est à la base un jeton rapide et votre approche actuelle peut suffire, bien qu'elle puisse être améliorée. Si les résultats ne sont pas conformes aux attentes, vous pouvez revenir en arrière et passer en revue.

Toutefois, si les résultats de votre codage informent l’orientation de votre recherche et que vous ne savez pas ce qu’ils devraient être, l’exactitude devient alors particulièrement importante. Une erreur dans votre code pourrait vous amener à tirer des conclusions erronées de votre expérience, avec diverses conséquences néfastes pour votre recherche globale.

Dans ce cas, diviser votre code en fonctions faciles à comprendre et à vérifier avec des tests unitaires vous donnera des briques de construction plus solides, ce qui vous donnera plus de confiance en vos résultats et vous épargnera beaucoup de frustration par la suite.


5

Si le contrôle de version et les tests unitaires permettent de garder votre code global organisé et fonctionnel, ils ne vous aident pas non plus à écrire du code plus propre.

  • Le contrôle de version vous permettra de voir quand et comment le code est devenu aussi désordonné.
  • Les tests unitaires garantiront que, même si le code est compliqué, il fonctionne toujours.

Si vous voulez vous empêcher d'écrire du code en désordre, vous avez besoin d'un outil qui fonctionne là où les dégâts se produisent: lorsque vous écrivez le code. Un type d'outil populaire qui s'appelle linter. Je ne suis pas un développeur Python, mais il semble que Pylint pourrait être une bonne option.

Un linter examine le code que vous avez écrit et le compare à un ensemble configurable de meilleures pratiques. Si le linter a une règle que les variables doivent être camelCaseet que vous en écrivez une snake_case, il le signalera comme une erreur. Les bons linters ont des règles allant de "les variables déclarées doivent être utilisées" à "La complexité cyclomatique des fonctions doit être inférieure à 3".

La plupart des éditeurs de code peuvent être configurés pour exécuter un linter chaque fois que vous enregistrez, ou simplement en général lors de la frappe, et indiquer les problèmes en ligne. Si vous tapez quelque chose comme x = 7, le xsera mis en surbrillance, avec l’instruction d’utiliser un nom plus long et meilleur (si c’est ce que vous avez configuré). Cela fonctionne comme une vérification orthographique dans la plupart des traitements de texte, ce qui rend difficile l’ignorance et permet de créer de meilleures habitudes.


Cela devrait avoir beaucoup plus de votes positifs. +1
bruyère

2
Mais, pour l'amour du ciel, assurez-vous que vous savez comment configurer le linter à un style qui vous convient, sinon il vous rendra fou de son agitation.
DrMcCleod

4

Tout ce que vous avez énuméré est un outil de la boîte à outils métaphorique. Comme n'importe quoi dans la vie, différents outils sont appropriés pour différentes tâches.

Comparé à d’autres domaines de l’ingénierie, le logiciel fonctionne avec un tas de pièces individuelles qui, en elles-mêmes, sont assez simples. Une déclaration de mission n'évalue pas différemment en fonction des fluctuations de température de la pièce. Une ifdéclaration ne se corrode pas à la place et retourne la même chose après un certain temps. Mais comme les éléments individuels sont très simples et que les logiciels sont conçus par des humains, ces éléments sont combinés en éléments de plus en plus grands jusqu'à ce que le résultat devienne si grand et complexe qu'il atteigne les limites de ce que les gens peuvent gérer mentalement.

Au fur et à mesure que les projets logiciels ont pris de l'ampleur, les gens les ont étudiés et ont créé des outils pour tenter de gérer cette complexité. OOP est un exemple. De plus en plus de langages de programmation abstraits sont un autre moyen. Parce qu'une grande partie de l'argent des logiciels en fait de plus en plus , les outils pour y parvenir sont ce que vous allez voir et lire. Mais il semble que ces situations ne vous concernent pas.

Alors, ne se sentent pas comme vous devez être faire tout cela. En fin de compte, le code n'est qu'un moyen de parvenir à une fin. Malheureusement, ce qui vous donnera le meilleur aperçu de ce qui est approprié ou non, est de travailler sur des projets plus importants, car il est beaucoup plus difficile de savoir ce qui manque lorsque vous pensez à la boîte à outils.

Dans tous les cas, je ne voudrais pas m'inquiéter de ne pas utiliser la POO ou d'autres techniques tant que vos scripts sont petits. La plupart des problèmes que vous avez décrits ne sont que des compétences organisationnelles professionnelles générales, c'est-à-dire que ne pas perdre un ancien fichier est un problème auquel tous les domaines doivent faire face.


4

En plus de toutes les bonnes suggestions fournies jusqu'à présent, une pratique que j'ai apprise au fil du temps et que je trouve essentielle consiste à ajouter très généreusement des commentaires détaillés à votre code. C'est la chose la plus importante pour moi quand je reviens à quelque chose après un long laps de temps. Explique-toi ce que tu penses. Cela prend un peu de temps à faire mais c'est relativement facile et surtout sans douleur.

J'ai parfois deux ou trois fois plus de lignes de commentaires que de code, en particulier lorsque les concepts ou les techniques sont nouveaux pour moi et doivent être expliqués par moi-même.

Faites le contrôle de version, améliorez vos pratiques, etc .... tout ce qui précède. Mais expliquez-vous les choses au fur et à mesure. Ça marche vraiment bien.


4

Quelles sont les qualités importantes pour ce type de programme?

Peu importe qu'il soit facile à maintenir ou à faire évoluer, il est fort probable que cela ne se produira pas.

Peu importe son efficacité, peu importe.

Peu importe qu'il ait une excellente interface utilisateur ou qu'il soit sécurisé contre des attaquants malveillants.

Il peut être important que ce soit lisible: quiconque lisant votre code puisse facilement se convaincre qu'il fait ce qu’il prétend faire.

Il est certainement important que ce soit correct. Si le programme donne des résultats incorrects, ce sont vos conclusions scientifiques qui sortent de la fenêtre. Mais il ne faut que traiter correctement l'entrée que vous lui demandez réellement de traiter; Peu importe vraiment si cela tombe si on donne des valeurs de données d'entrée négatives, si toutes vos valeurs de données sont positives.

Il importe également que vous mainteniez un certain niveau de contrôle du changement. Vos résultats scientifiques doivent être reproductibles, ce qui signifie que vous devez savoir quelle version du programme a produit les résultats que vous souhaitez publier. Comme il n'y a qu'un seul développeur, le contrôle de modification n'a pas besoin d'être très élaboré, mais vous devez vous assurer que vous pouvez revenir à un moment donné et reproduire vos résultats.

Donc, ne vous inquiétez pas des paradigmes de programmation, de l'orientation des objets, de l'élégance algorithmique. Ne vous souciez que de la clarté et de la lisibilité et de la traçabilité de vos modifications dans le temps. Ne vous inquiétez pas pour l'interface utilisateur. Ne vous inquiétez pas pour tester toutes les combinaisons possibles de paramètres d'entrée, mais faites suffisamment d'essais pour vous assurer (et pour donner confiance aux autres) que vos résultats et conclusions sont valides.


4

J'ai travaillé dans un environnement similaire avec des universitaires qui écrivent beaucoup de code (mathématiques / sciences), mais leur progression est lente pour les mêmes raisons que celles que vous avez décrites. Cependant, j’ai remarqué une chose qui s’est bien déroulée et qui, à mon avis, peut également vous aider: constituer et maintenir une collection de bibliothèques spécialisées pouvant être utilisées dans plusieurs projets. Ces bibliothèques devraient fournir des fonctions utilitaires et vous aideront donc à garder votre projet actuel spécifique au domaine problématique.

Par exemple, vous devrez peut-être gérer de nombreuses transformations de coordonnées dans votre domaine (ECEF, NED, lat / lon, WGS84, etc.), de sorte qu'une fonction similaire convert_ecef_to_ned()devrait être insérée dans un nouveau projet appelé CoordinateTransformations. Placez le projet sous contrôle de version et hébergez-le sur les serveurs de votre service afin que d'autres personnes puissent l'utiliser (et éventuellement l'améliorer). Ensuite, après quelques années, vous devriez avoir une collection solide de bibliothèques avec vos projets ne contenant que du code spécifique à un problème / domaine de recherche particulier.

Quelques conseils plus généraux:

  • Toujours viser à modéliser votre problème particulier aussi précisément que possible, peu importe ce que c'est. De cette façon, les questions de conception de logiciel telles que quoi / où / comment mettre une variable devraient devenir beaucoup plus évidentes à répondre.
  • Je ne me soucierais pas du développement piloté par les tests, car le code scientifique décrit des idées et des concepts et est plus créatif et fluide; il n'y a pas d'API à définir, de services à maintenir, de risques pour le code d'autrui lors du changement de fonctionnalité, etc.

Ne laissez pas les autres l’améliorer. Les chances sont qu'ils ne comprennent pas le but du code et ils vont tout gâcher.
Mathreadler

@mathreadler Eh bien, s'il s'agit de bibliothèques d'utilitaires généraux, il sera un peu difficile pour les autres de gâcher, c'est l'idée.
Jigglypuff

Pourquoi est-il difficile de gâcher les bibliothèques à usage général? Ce n'est pas si difficile si vous n'avez aucune idée de ce que vous faites, ou si vous faites de gros efforts, d'ailleurs.
mathreadler

@mathreadler Parce qu'il n'y a généralement qu'un seul moyen d'effectuer des transformations de coordonnées ou des conversions d'unités, par exemple.
Jigglypuff

Il existe généralement de nombreuses façons selon la manière dont vos numéros sont stockés dans la mémoire, la représentation qu’ils utilisent et de nombreux autres éléments, pour quel processeur vous souhaitez compiler la bibliothèque. Un codeur peut supposer que tout le monde utilisera toujours des doublons IEEE par exemple, mais un autre utilise presque toujours une simple précision ou un troisième format plus étrange. Un codeur utilisera alors le polymorphisme de modèle, mais un autre pourrait y être allergique, un troisième, même plus étrange, codera en dur tout en c ou en assembleur.
Mathreadler

3

Ce qui suit sont mes opinions et très influencés par mon propre chemin.

Le codage engendre souvent des perspectives dogmatiques sur la manière de faire les choses. Au lieu de techniques et d’outils, je pense que vous devez examiner les valeurs et les coûts cumulés pour décider d’une stratégie appropriée.

Écrire un code solide, lisible, débogable et solide demande beaucoup de temps et d’efforts. Dans de nombreux cas, étant donné un horizon de planification limité, cela n’est pas rentable (paralysie de l’analyse).

Un collègue avait une règle de base; si vous faites essentiellement le même genre de chose pour la troisième fois, investissez des efforts, sinon un travail rapide et sale est approprié.

Des tests de quelque sorte sont essentiels, mais pour des projets ponctuels, une simple observation peut suffire. Pour tout ce qui est substantiel, des tests et une infrastructure de test sont essentiels. La valeur est que cela vous libère lors du codage, le coût est que si le test est axé sur une implémentation particulière, les tests ont également besoin de maintenance. Les tests vous rappellent également comment les choses sont supposées fonctionner.

Pour mes propres scripts uniques (souvent pour des tâches telles que la validation d'une estimation d'une probabilité, ou similaire), j'ai trouvé deux petites choses très utiles: 1. Incluez un commentaire indiquant comment le code est utilisé. 2. Incluez une brève description des raisons pour lesquelles vous avez écrit le code. Ces choses-là sont terriblement évidentes quand vous écrivez le code, mais l'évidence perd de son temps :-).

La POO concerne la réutilisation de code, l'abstraction, l'encapsulation, la factorisation, etc. Très utile, mais il est facile de s'y perdre si la production de code et de design de qualité n'est pas votre objectif final. Il faut du temps et des efforts pour produire des choses de qualité.


3

Bien que je pense que les tests unitaires ont leurs mérites, ils ont une valeur douteuse pour le développement scientifique - ils sont souvent trop petits pour offrir beaucoup de valeur.

Mais j'aime beaucoup les tests d'intégration de code scientifique:

Isolez une petite partie de votre code qui pourrait fonctionner seul, par exemple le pipeline ETL. Ecrivez ensuite un test fournissant les données, exécutez le pipeline etl (ou simplement une étape), puis vérifiez que le résultat correspond à vos attentes. Bien que le bloc testé puisse contenir beaucoup de code, le test fournit toujours une valeur:

  1. Vous avez un crochet pratique pour ré-exécuter votre code, ce qui permet de l’exécuter souvent.
  2. Vous pouvez tester certaines hypothèses dans votre test
  3. Si quelque chose ne va pas, il est facile d’ajouter un test qui échoue et de corriger le problème.
  4. Vous codifiez les entrées / sorties attendues, en évitant le mal de tête habituel résultant de la tentative de deviner le format des données en entrée.
  5. Même s'ils ne sont pas aussi simples que les tests unitaires, les tests informatiques vous permettent néanmoins de décomposer votre code et de vous forcer à ajouter des limites à votre code.

J'utilise souvent cette technique et aboutis souvent à une fonction principale relativement lisible, mais les sous-fonctions sont souvent assez longues et laides, mais peuvent être modifiées et réorganisées rapidement en raison de la robustesse des limites d'E / S.


2

Je travaille normalement sur une très grande base source. Nous utilisons tous les outils que vous mentionnez. Récemment, j'ai commencé à travailler sur des scripts python pour un projet parallèle. Ils sont quelques dizaines à quelques centaines de lignes au plus. Par habitude, j'ai engagé mes scripts dans le contrôle de code source. Cela a été utile car je peux créer des branches pour expérimenter des expériences qui pourraient ne pas fonctionner. Je peux créer un fork si j'ai besoin de dupliquer le code et de le modifier dans un autre but. Cela laisse l'original intacte au cas où je devrais le sortir à nouveau.

Pour les "tests unitaires", je n'ai que quelques fichiers d'entrée destinés à produire des sorties connues que je vérifie à la main. Je pourrais probablement l'automatiser, mais il me semble que cela prendrait plus de temps que je n'en économiserais. Cela dépend probablement de la fréquence à laquelle je dois modifier et exécuter les scripts. De toute façon, si cela fonctionne, faites-le. Si le problème ne vous en vaut pas la peine, ne perdez pas votre temps.


2

Avec l'écriture de code - comme avec l'écriture en général - la question principale est:

Quel lecteur avez-vous en tête? ou qui consomme votre code?

Des règles telles que les directives de codage officielles n’ont aucun sens lorsque vous êtes votre seul public.

Cela étant dit, par contre, il serait utile de rédiger le code de manière à ce que votre avenir puisse le comprendre tout de suite.

Donc, un "bon style" serait celui qui vous aiderait le plus. À quoi ce style devrait ressembler est une réponse que je ne peux pas donner.

Je pense que vous n'avez pas besoin de tests de programmation orientée objet ni de tests unitaires pour des fichiers de 150 LOC. Un VCS dédié serait intéressant si vous avez un code en évolution. Sinon, un .bakfait le tour. Ces outils sont un remède à une maladie, vous pourriez même pas avoir.

Vous devriez peut-être écrire votre code de telle manière que, même si vous le lisez en état d'ébriété, vous puissiez le lire, le comprendre et le modifier.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.