Sauberes Programmieren beim Schreiben von wissenschaftlichem Code


169

Ich schreibe keine großen Projekte. Ich verwalte keine riesige Datenbank und beschäftige mich nicht mit Millionen von Codezeilen.

Mein Code ist in erster Linie "Skripting" - Dinge zum Testen von mathematischen Funktionen oder zum Simulieren von etwas - "wissenschaftliches Programmieren". Die längsten Programme, an denen ich bis jetzt gearbeitet habe, sind ein paar hundert Codezeilen, und die meisten Programme, an denen ich arbeite, sind ungefähr 150.

Mein Code ist auch Mist. Ich habe das neulich bemerkt, als ich versucht habe, eine Datei zu finden, die ich vor einiger Zeit geschrieben habe, die ich aber wahrscheinlich überschrieben habe und die ich nicht mit Versionskontrolle verwende.

Der Stil meines Codes ist verworren und mit veralteten Kommentaren gefüllt, die alternative Vorgehensweisen angeben, oder mit Codezeilen, die überschrieben werden. Während die Variablennamen immer sehr nett und beschreibend beginnen, wenn ich Dinge hinzufüge oder ändere, wie z. B. etwas Neues, das jemand testen möchte, wird Code darüber gelegt und überschrieben, und weil ich der Meinung bin, dass dieses Ding jetzt schnell getestet werden sollte Ich habe ein Framework, in dem ich anfange, beschissene Variablennamen zu verwenden, und die Datei geht in den Topf.

In dem Projekt, an dem ich gerade arbeite, bin ich in der Phase, in der all das zurückkommt, um mich großartig zu beißen. Aber das Problem ist (abgesehen von der Verwendung der Versionskontrolle und dem Erstellen einer neuen Datei für jede neue Iteration und dem Aufzeichnen aller Dateien in einer Textdatei, was die Situation wahrscheinlich dramatisch verbessern wird), dass ich nicht wirklich weiß, wie ich mit der Verbesserung fortfahren soll Mein eigentlicher Codierungsstil.

Ist ein Komponententest erforderlich, um kleinere Codeteile zu schreiben? Wie wäre es mit OOP? Welche Ansätze eignen sich, um bei "wissenschaftlichem Programmieren" guten, sauberen Code schnell zu schreiben, anstatt an größeren Projekten zu arbeiten?

Ich stelle diese Fragen, weil die Programmierung selbst oft nicht sehr komplex ist. Es geht eher um Mathematik oder Naturwissenschaften, die ich mit der Programmierung teste oder erforsche. Ist zB eine Klasse notwendig, wenn zwei Variablen und eine Funktion sich wahrscheinlich darum kümmern könnten? (Bedenken Sie, dass dies im Allgemeinen auch Situationen sind, in denen die Geschwindigkeit des Programms am schnellsten ist. Wenn Sie mehr als 25.000.000 Zeitschritte einer Simulation ausführen, möchten Sie, dass dies der Fall ist.)

Vielleicht ist das zu weit gefasst, und wenn ja, entschuldige ich mich, aber wenn ich mir Programmbücher anschaue, scheinen sie oft bei größeren Projekten angesprochen zu werden. Mein Code nicht braucht OOP, und es ist schon verdammt kurz , so ist es nicht wie „oh, aber die Datei wird von tausend Zeilen reduziert werden , wenn wir das tun!“ Ich möchte wissen, wie ich bei diesen kleineren, schnelleren Projekten von vorne anfangen und sauber programmieren kann.

Ich würde gerne genauere Angaben machen, aber je allgemeiner der Rat, desto nützlicher, denke ich. Ich programmiere in Python 3.


Jemand schlug ein Duplikat vor. Lassen Sie mich klarstellen, dass es nicht darum geht, Standard-Programmierstandards direkt zu ignorieren. Es gibt natürlich einen Grund, warum diese Standards existieren. Aber auf der anderen Seite macht es wirklich Sinn, Code zu schreiben, der besagt, OOP, wenn einige Standard-Dinge hätten erledigt werden können, viel schneller zu schreiben gewesen wären und aufgrund der Kürze des Codes eine ähnliche Lesbarkeit gehabt hätten Programm?

Es gibt Ausnahmen. Darüber hinaus gibt es wahrscheinlich Standards für die wissenschaftliche Programmierung, die über reine Standards hinausgehen. Ich frage auch danach. Hier geht es nicht darum, ob normale Codierungsstandards beim Schreiben von wissenschaftlichem Code ignoriert werden sollten, sondern darum, sauberen wissenschaftlichen Code zu schreiben!


Aktualisieren

Ich dachte nur, ich würde eine Art Update "nicht ganz eine Woche später" hinzufügen. Alle Ihre Ratschläge waren äußerst hilfreich. Ich benutze jetzt Versionskontrolle - git mit git kraken für eine grafische Oberfläche. Es ist sehr einfach zu bedienen und hat meine Dateien drastisch aufgeräumt - es müssen keine alten Dateien mehr herumstehen oder alte Codeversionen "nur für den Fall" auskommentiert werden.

Ich habe auch pylint installiert und auf meinem gesamten Code ausgeführt. Eine Datei wurde anfangs negativ bewertet. Ich bin mir nicht mal sicher, wie das möglich war. Meine Hauptdatei begann mit einer Punktzahl von ~ 1,83 / 10 und liegt jetzt bei ~ 9,1 / 10. Der gesamte Code entspricht nun ziemlich gut den Standards. Ich überflog es auch mit meinen eigenen Augen und aktualisierte Variablennamen, die ... ähm ... schief gelaufen waren, und suchte nach Abschnitten, die umgestaltet werden sollten.

Insbesondere habe ich kürzlich auf dieser Website eine Frage zum Refactoring einer meiner Hauptfunktionen gestellt, und jetzt ist sie viel übersichtlicher und viel kürzer: Statt einer langen, aufgeblähten, wenn / sonst gefüllten Funktion ist sie jetzt weniger als die Hälfte die Größe und viel einfacher herauszufinden, was los ist.

Mein nächster Schritt ist die Implementierung von "Unit-Tests". Damit meine ich eine Datei, die ich auf meiner Hauptdatei ausführen kann, die alle darin enthaltenen Funktionen mit assert-Anweisungen und try / excepts anzeigt, was wahrscheinlich nicht der beste Weg ist, dies zu tun, und zu einer Menge doppeltem Code führt. aber ich lese weiter und versuche herauszufinden, wie ich es besser machen kann.

Ich habe auch die Dokumentation, die ich bereits geschrieben habe, erheblich aktualisiert und dem Github-Repository zusätzliche Dateien wie eine Excel-Tabelle, die Dokumentation und ein zugehöriges Dokument hinzugefügt. Es sieht jetzt aus wie ein echtes Programmierprojekt.

Also ... ich denke, das ist alles zu sagen: Danke .




8
Wenn Sie Ihren Code aktiv verbessern möchten, können Sie einige in Code Review veröffentlichen . Die Community dort hilft Ihnen gerne dabei.
Hoffmale

7
Wenn Sie sagen: "Abgesehen von der Verwendung der Versionskontrolle und dem Erstellen einer neuen Datei für jede neue Iteration und dem Aufzeichnen aller Dateien in einer Textdatei irgendwo" mit "und" meinen Sie "oder", denn wenn Sie die Versionskontrolle verwenden, sollten Sie dies nicht tun keine Copy-Paste-Versionen sein. Der Punkt ist, dass die Versionskontrolle die alte Version für Sie behält
Richard Tingle

2
@mathreadler Ich glaube du verstehst das nicht ganz. Ja, nur ich werde wahrscheinlich jeden lesen und mit dem Code herumspielen (obwohl du es nie weißt, und ich habe jemanden, mit dem ich zusammenarbeite, der programmieren kann, wenn auch in einer anderen Sprache) ... aber der Code ist immer noch Mist. Ich muss es später lesen und noch einmal herausfinden, was zum Teufel ich tue. Es ist ein Problem, und ich kann es bezeugen, weil ich die Effekte jetzt erlebe und die Dinge einfacher geworden sind, als ich die Versionskontrolle und andere hier vorgeschlagene Techniken implementiert habe.
Heather

Antworten:


163

Dies ist ein ziemlich häufiges Problem für Wissenschaftler. Ich habe es oft gesehen, und es ist immer darauf zurückzuführen, dass Sie sich nebenbei für das Programmieren als Werkzeug für Ihre Arbeit entscheiden.

Ihre Skripte sind also ein Durcheinander. Ich werde gegen den gesunden Menschenverstand gehen und sagen, dass es nicht so schlimm ist , vorausgesetzt, Sie programmieren alleine ! Sie werden das meiste von dem, was Sie schreiben, nie wieder anfassen, also verbringen Sie zu viel Zeit damit, hübschen Code zu schreiben, anstatt "Wert" zu produzieren (das Ergebnis Ihres Skripts), wird Ihnen nicht viel antun.

Es wird jedoch eine Zeit geben, in der Sie zu etwas zurückkehren müssen, das Sie getan haben, und genau sehen müssen, wie etwas funktioniert hat. Wenn andere Wissenschaftler Ihren Code überprüfen müssen, ist es außerdem sehr wichtig, dass er so klar und präzise wie möglich ist, damit jeder ihn verstehen kann.

Ihr Hauptproblem wird die Lesbarkeit sein. Hier einige Tipps zur Verbesserung:

Variablennamen:

Wissenschaftler lieben es, prägnante Notationen zu verwenden. Alle mathematischen Gleichungen verwenden normalerweise einzelne Buchstaben als Variablen, und ich wäre nicht überrascht, wenn Ihr Code viele, viele sehr kurze Variablen enthält. Dies schadet der Lesbarkeit sehr. Wenn Sie zu Ihrem Code zurückkehren, werden Sie sich nicht daran erinnern, was y, i und x2 darstellen, und Sie werden viel Zeit damit verbringen, es herauszufinden. Versuchen Sie stattdessen, Ihre Variablen explizit zu benennen und Namen zu verwenden, die genau das darstellen, was sie sind.

Teilen Sie Ihren Code in Funktionen auf:

Nachdem Sie alle Variablen umbenannt haben, sehen Ihre Gleichungen schrecklich aus und sind mehrzeilig.

Verschieben Sie die Gleichung in eine andere Funktion, anstatt sie in Ihrem Hauptprogramm zu belassen, und benennen Sie sie entsprechend. Anstatt eine riesige und durcheinandergebrachte Codezeile zu haben, erhalten Sie jetzt eine kurze Anleitung, die Ihnen genau erklärt, was los ist und welche Gleichung Sie verwendet haben. Dies verbessert sowohl Ihr Hauptprogramm, da Sie sich nicht einmal die tatsächliche Gleichung ansehen müssen, um zu wissen, was Sie getan haben, als auch den Gleichungscode selbst, da Sie in einer separaten Funktion Ihre Variablen benennen können, wie Sie möchten, und zurück zu die bekannteren einzelnen Buchstaben.

Versuchen Sie auf dieser Gedankenlinie, alle Teile des Codes herauszufinden, die für etwas stehen, insbesondere, wenn es sich um etwas handelt, das Sie mehrmals in Ihrem Code ausführen müssen, und teilen Sie sie in Funktionen auf. Sie werden feststellen, dass Ihr Code schnell leichter zu lesen ist und Sie dieselben Funktionen verwenden können, ohne mehr Code schreiben zu müssen.

Icing on the cake, wenn diese Funktionen in mehr Ihrer Programme benötigt werden, können Sie einfach eine Bibliothek für sie erstellen, und Sie werden sie immer verfügbar haben.

Globale Variablen:

Als Anfänger dachte ich, dies sei eine großartige Möglichkeit, Daten weiterzugeben, die ich in vielen Punkten meines Programms benötigte. Es hat sich herausgestellt, dass es viele andere Möglichkeiten gibt, Dinge zu umgehen, und die einzigen Dinge, die globale Variablen tun, sind Kopfschmerzen, da Sie niemals wissen werden, wann dieser Wert zuletzt verwendet oder bearbeitet wurde, und wenn Sie zu einem zufälligen Punkt Ihres Programms gehen es aufzuspüren wird ein Schmerz sein. Versuchen Sie, sie nach Möglichkeit zu meiden.

Wenn Ihre Funktionen mehrere Werte zurückgeben oder ändern müssen, erstellen Sie entweder eine Klasse mit diesen Werten und übergeben Sie sie als Parameter, oder veranlassen Sie die Funktion, mehrere Werte (mit benannten Tupeln) zurückzugeben und diese Werte im Aufrufercode zuzuweisen.

Versionskontrolle

Dadurch wird die Lesbarkeit nicht direkt verbessert, Sie können jedoch alle oben genannten Aufgaben ausführen. Wenn Sie Änderungen vornehmen, müssen Sie sich an die Versionskontrolle halten (ein lokales Git-Repository reicht aus), und wenn etwas nicht funktioniert, sehen Sie sich an, was Sie geändert haben, oder machen Sie einfach einen Rollback! Dies erleichtert die Umgestaltung Ihres Codes und ist ein Sicherheitsnetz, wenn Sie versehentlich etwas kaputt machen.

Wenn Sie all dies berücksichtigen, können Sie klaren und effektiveren Code schreiben und mögliche Fehler schneller finden, da Sie sich nicht durch riesige Funktionen und chaotische Variablen wühlen müssen.


56
Exzellente Beratung. Allerdings kann ich den Kommentar "Das ist nicht so schlimm" nicht befürworten. Es ist so schlecht. Wissenschaftliche Skripte von geringer Qualität sind ein großes Problem für die Reproduzierbarkeit bei der Datenanalyse und eine häufige Fehlerquelle bei der Analyse. Das Schreiben von gutem Code ist nicht nur wertvoll, um ihn später zu verstehen, sondern auch, um Fehler zu vermeiden.
Jack Aidley

22
Wenn der Code eine Implementierung einer bekannten Formel ist, sind Variablennamen mit einem Buchstaben und dergleichen möglicherweise die richtige Vorgehensweise. Kommt auf das Publikum an ... und mehr auf den Punkt, ob der Leser schon wissen soll, was die Namen bedeuten.
CHAO

11
@cHao das Problem ist nicht, wenn diese Variablen in die Formel eingesteckt sind (daher der Hinweis "Benenne sie innerhalb der Funktion um"), sondern wenn sie außerhalb davon gelesen und bearbeitet werden und wenn sie anfangen, mit anderen Variablen in Konflikt zu geraten (Ich habe zum Beispiel gesehen, dass Leute, die drei "x"
-Variablen

4
"Ich werde gegen den gesunden Menschenverstand gehen ..." Nein, bist du nicht. Sie wenden sich gegen das vorherrschende Dogma, das selbst gegen den gesunden Menschenverstand verstößt. ;) Das ist alles ein perfekter Rat.
jpmc26

3
Als (ehemaliger) wissenschaftlicher Programmierer wende ich in der Regel eine Dreierregel an. Wenn ich dreimal ähnlichen Code schreibe, wird die Funktionalität in einem separaten Modul mit Dokumentation (oft nur Kommentare, aber das ist ausreichend) beschrieben. Dies schränkt das Chaos der Ad-hoc-Programmierung ein und ermöglicht mir den Aufbau einer Bibliothek, die ich in Zukunft erweitern kann.
Rcollyer

141

Physiker hier. War dort.

Ich würde argumentieren, dass es bei Ihrem Problem nicht um die Auswahl von Tools oder Programmierparadigmen geht (Unit Testing, OOP, was auch immer). Es geht um die Einstellung , die Denkweise. Die Tatsache, dass Ihre Variablennamen anfangs gut gewählt sind und am Ende Mist sind, ist aufschlussreich genug. Wenn Sie Ihren Code als "einmal ausführen, dann wegwerfen" betrachten, wird es unvermeidlich ein Durcheinander sein. Wenn Sie es als das Produkt von Handwerk und Liebe betrachten, wird es schön sein.

Ich glaube, es gibt nur ein Rezept für das Schreiben von sauberem Code: Schreiben Sie es für den Menschen, der es lesen wird, nicht für den Interpreter, der es ausführen wird. Dem Interpreter ist es egal, ob Ihr Code ein Chaos ist, aber der menschliche Leser kümmert sich darum.

Sie sind Wissenschaftler. Sie können wahrscheinlich viel Zeit damit verbringen, einen wissenschaftlichen Artikel zu polieren. Wenn Ihr erster Entwurf kompliziert aussieht, werden Sie ihn umgestalten, bis die Logik auf die natürlichste Art und Weise fließt. Sie möchten, dass Ihre Kollegen es lesen und die Argumente kristallklar finden. Sie möchten, dass Ihre Schüler in der Lage sind, daraus zu lernen.

Das Schreiben von sauberem Code ist genau dasselbe. Stellen Sie sich Ihren Code als detaillierte Erklärung eines Algorithmus vor, der nur gelegentlich maschinenlesbar ist. Stellen Sie sich vor, Sie werden es als Artikel veröffentlichen, den die Leute lesen werden. Sie werden es sogar auf einer Konferenz zeigen und das Publikum Zeile für Zeile durchlaufen lassen. Nun proben Sie Ihre Präsentation . Ja, Zeile für Zeile ! Peinlich, nicht wahr? Räumen Sie also Ihre Folien auf (äh ... ich meine, Ihren Code) und proben Sie noch einmal. Wiederholen, bis Sie mit dem Ergebnis zufrieden sind.

Es wäre immer noch besser, wenn Sie nach den Proben Ihren Code echten Menschen zeigen könnten, anstatt nur imaginären Menschen und Ihrem zukünftigen Selbst. Das zeilenweise Durchgehen wird als „Code Walk“ bezeichnet und ist keine dumme Übung.

All dies hat natürlich einen Preis. Das Schreiben von sauberem Code nimmt viel mehr Zeit in Anspruch als das Schreiben von Wegwerfcode. Nur Sie können beurteilen, ob der Nutzen die Kosten für Ihren speziellen Anwendungsfall überwiegt.

Was die Werkzeuge betrifft, sagte ich, bevor sie nicht so wichtig sind. Wenn ich jedoch eine auswählen müsste, würde ich sagen, dass die Versionskontrolle die nützlichste ist.


32
«Sauberen Code zu schreiben ist genau das Gleiche wie einen klaren Artikel zu schreiben.» Das unterstütze ich voll und ganz.
Juandesant

43
Dies ist etwas, was die meisten professionellen Programmierer vergessen zu sagen, weil es so offensichtlich ist. Ihr Code ist fertig, wenn er von einem anderen Programmierer gelesen und geändert werden kann. Nicht, wenn es läuft und die richtige Ausgabe erzeugt. Das OP muss eine zusätzliche Stunde pro Skript aufwenden, um seinen Code zu überarbeiten und zu kommentieren, damit er von Menschen gelesen werden kann.
UEFI

31
Obwohl das Schreiben von sauberem Code viel mehr Zeit in Anspruch nimmt als das Schreiben von Wegwerfcode, ist es viel wichtiger, dass das Lesen von Wegwerfcode viel mehr Zeit in Anspruch nimmt als das Lesen von sauberem Code.
user949300

3
@UEFI Nein, das merken die meisten professionellen Programmierer gar nicht. Oder kümmere dich nicht darum.
jpmc26

2
Stimme zu 100%. Der Statistiker wurde zum Programmierer, deshalb programmiere ich ziemlich viel 'wissenschaftlich'. Klare Codes mit aussagekräftigen Kommentaren sind lebensrettend, wenn Sie nach 1, 4 oder 12 Monaten zu diesem Code zurückkehren müssen. Wenn Sie den Code lesen, erfahren Sie, was der Code tut. Wenn Sie die Kommentare lesen, erfahren Sie, was der Code tun soll.
Railsdog

82

Die Versionskontrolle wird Ihnen wahrscheinlich den größten Gewinn bringen. Es ist nicht nur für die Langzeitspeicherung gedacht, sondern auch zum Nachverfolgen Ihrer kurzfristigen Experimente und zum Zurückkehren zur letzten funktionierenden Version, wobei Notizen auf dem Weg gemacht werden.

Als nächstes sind Unit-Tests am nützlichsten. Die Sache bei Unit-Tests ist, dass sogar Codebasen mit Millionen von Codezeilen eine Funktion nach der anderen Unit-getestet werden. Unit-Tests werden auf der kleinsten Abstraktionsebene durchgeführt. Das heißt, es gibt grundsätzlich keinen Unterschied zwischen Unit-Tests, die für kleine Codebasen geschrieben wurden, und solchen, die für große Codebasen verwendet werden. Es gibt nur mehr von ihnen.

Unit-Tests sind der beste Weg, um zu verhindern, dass etwas kaputt geht, das bereits funktioniert hat, wenn Sie etwas anderes reparieren, oder um Ihnen zumindest schnell mitzuteilen, wann Sie dies tun. Sie sind eigentlich mehr nützlich , wenn Sie nicht als qualifizierte Programmierer sind, oder nicht wissen , wie oder wollen nicht ausführlichen Code zu schreiben , das strukturiert ist , um Fehler weniger wahrscheinlich oder deutlicher zu machen.

Zwischen der Versionskontrolle und dem Schreiben von Unit-Tests wird Ihr Code natürlich viel sauberer. Andere Techniken zur saubereren Codierung können erlernt werden, wenn Sie ein Plateau erreichen.


78
Ich habe den größten Teil meines Codes religiös getestet, aber ich habe herausgefunden, dass der explorative wissenschaftliche Code für das Testen von Einheiten weniger als nutzlos ist . Die Methodik scheint hier grundsätzlich nicht zu funktionieren. Ich kenne keinen Computerwissenschaftler auf meinem Gebiet, der seinen Analysecode einem Unit-Test unterzieht. Ich bin mir nicht sicher, was der Grund für diese Nichtübereinstimmung ist, aber einer der Gründe ist sicherlich, dass es schwierig oder unmöglich ist, gute Testfälle zu etablieren, außer für triviale Einheiten.
Konrad Rudolph

22
@KonradRudolph Der Trick in diesen Fällen ist wahrscheinlich die saubere Trennung von Bedenken zwischen Teilen Ihres Codes, die ein klar definierbares Verhalten aufweisen (lesen Sie diese Eingabe, berechnen Sie diesen Wert), und Teilen Ihres Codes, die entweder wirklich explorativ sind oder sich an z Einige für Menschen lesbare Ausgaben oder Visualisierungen. Das Problem hierbei ist wahrscheinlich, dass eine unzureichende Trennung der Bedenken dazu führt, dass diese Linien verwischt werden, was zu der Annahme führt, dass Unit-Tests in diesem Kontext nicht möglich sind, was Sie in einem sich wiederholenden Zyklus zum Start zurückführt.
Ant P

18
Nebenbei bemerkt funktioniert die Versionskontrolle auch bei LaTeX-Dokumenten recht gut, da das Format für Textunterschiede geeignet ist. Auf diese Weise können Sie ein Repository für Ihre Papiere und den Code, der sie unterstützt, haben. Ich schlage vor, verteilte Versionskontrolle wie Git zu untersuchen. Es gibt eine gewisse Lernkurve, aber sobald Sie es verstanden haben, haben Sie eine gute Möglichkeit, Ihre Entwicklung zu wiederholen, und Sie haben einige interessante Möglichkeiten, eine Plattform wie Github zu verwenden, die kostenlose Teamkonten für Akademiker bietet .
Dan Bryant

12
@AntP Es ist möglich, dass es einfach nicht so viel Code gibt, der sinnvoll in gut definierte testbare Einheiten umgewandelt werden kann. Ein großer Teil des wissenschaftlichen Codes fasst eine Reihe von Bibliotheken zusammen. Diese Bibliotheken werden bereits gut getestet und sauber strukturiert sein, was bedeutet, dass der Autor nur "Kleber" schreiben muss, und meiner Erfahrung nach ist es nahezu unmöglich, Komponententests für Kleber zu schreiben, die nicht tautologisch sind.
James_pic

7
"Zwischen der Versionskontrolle und dem Schreiben von Unit-Tests wird Ihr Code natürlich viel sauberer." Das stimmt nicht . Ich kann es persönlich bestätigen. Keines dieser Tools hindert Sie daran, beschissenen Code zu schreiben, und insbesondere das Schreiben beschissener Tests auf beschissenen Code erschwert die Bereinigung nur noch mehr. Tests sind keine Wundermittel, und es ist eine schreckliche Sache, mit jedem Entwickler zu reden, der noch etwas lernt (das ist jeder). Die Versionskontrolle verursacht im Allgemeinen niemals Schäden am Code selbst, wie dies bei schlechten Tests der Fall ist.
jpmc26

29

(Abgesehen von der Verwendung der Versionskontrolle und dem Erstellen einer neuen Datei für jede neue Iteration und dem Aufzeichnen aller Dateien in einer Textdatei, was die Situation wahrscheinlich dramatisch verbessern wird)

Sie hätten das wahrscheinlich selbst herausgefunden, aber wenn Sie " alles irgendwo in einer Textdatei aufzeichnen müssen ", nutzen Sie das Versionskontrollsystem nicht in vollem Umfang. Verwenden Sie etwas wie Subversion, Git oder Mercurial und schreiben Sie bei jedem Commit eine gute Commit-Nachricht. Sie erhalten ein Protokoll, das dem Zweck der Textdatei dient, aber nicht vom Repository getrennt werden kann.

Abgesehen davon ist die Verwendung der Versionskontrolle das Wichtigste, was Sie aus einem Grund tun können, der in keiner der vorhandenen Antworten erwähnt wird: Reproduzierbarkeit der Ergebnisse . Wenn Sie entweder Ihre Protokollnachrichten verwenden oder den Ergebnissen eine Notiz mit der Versionsnummer hinzufügen können, können Sie sicher sein, dass Sie die Ergebnisse erneut generieren können, und Sie sind besser in der Lage, den Code mit dem Papier zu veröffentlichen.

Ist ein Komponententest erforderlich, um kleinere Codeteile zu schreiben? Wie wäre es mit OOP? Welche Ansätze eignen sich, um bei "wissenschaftlichem Programmieren" guten, sauberen Code schnell zu schreiben, anstatt an größeren Projekten zu arbeiten?

Komponententests sind nie notwendig, aber es ist nützlich, wenn (a) der Code so modular ist, dass Sie Einheiten und nicht das Ganze testen können; (b) Sie können Tests erstellen. Im Idealfall können Sie die erwartete Ausgabe von Hand schreiben, anstatt sie mit dem Code zu generieren. Wenn Sie sie jedoch mit Code generieren, erhalten Sie zumindest Regressionstests, mit denen Sie feststellen können, ob sich das Verhalten von etwas geändert hat. Überlegen Sie sich nur, ob die Tests mit größerer Wahrscheinlichkeit fehlerhaft sind als der Code, den sie testen.

OOP ist ein Werkzeug. Verwenden Sie es, wenn es hilft, aber es ist nicht das einzige Paradigma. Ich gehe davon aus, dass Sie die prozedurale Programmierung nur wirklich kennen: Wenn dies der Fall ist, dann würden Sie in dem beschriebenen Kontext meiner Meinung nach mehr vom Studium der funktionalen Programmierung als von der OOP profitieren, insbesondere von der Disziplin, nach Möglichkeit Nebenwirkungen zu vermeiden. Python kann in einem sehr funktionalen Stil geschrieben werden.


4
+1 für Commit-Nachrichten; Sie sind wie Kommentare, die nicht veraltet sein können, weil sie an die Version des Codes gebunden sind, als sie tatsächlich anwendbar waren. Um Ihren alten Code zu verstehen, ist es einfacher, den Verlauf des Projekts durchzusehen (wenn Änderungen mit einer angemessenen Genauigkeit übernommen werden), als veraltete Kommentare zu lesen.
Dumme Freak

Subversion, Git und Mercurial sind nicht fungibel. Ich würde dringend empfehlen, Git (oder Mercurial) mit einem lokalen Repository über Subversion zu verwenden. Mit einem Solo-Coder sind die Fehler von Subversion weniger ein Problem, aber es ist kein großartiges Werkzeug für die gemeinsame Entwicklung und das kann möglicherweise in der Forschung passieren
mcottle

2
@mcottle, ich persönlich bevorzuge Git, aber ich glaube nicht, dass dies der richtige Ort ist, um auf die Unterschiede einzugehen, zumal die Wahl einer der aktiven Religionskriege ist. Es ist besser, OP zu ermutigen, etwas zu benutzen , als sie aus dem Gebiet zu vertreiben, und die Entscheidung ist in keinem Fall dauerhaft.
Peter Taylor

21

In der Graduiertenschule habe ich selbst einen algorithmischen Code geschrieben. Es ist ein bisschen eine harte Nuss zu knacken. Um es grob auszudrücken: Viele Programmierkonventionen basieren auf der Idee, Informationen in eine Datenbank einzufügen, sie zum richtigen Zeitpunkt abzurufen und diese Daten dann zu massieren, um sie einem Benutzer zu präsentieren. In der Regel wird eine Bibliothek für mathematische Zwecke verwendet algorithmische Teile dieses Prozesses. Für diese Programme ist alles, was Sie über OOP gehört haben, Code in kurze Funktionen zu unterteilen und alles auf einen Blick leicht verständlich zu machen, wo dies möglich ist, ein ausgezeichneter Rat. Aber es funktioniert nicht ganz für algorithmischen Code oder Code, der komplexe mathematische Berechnungen implementiert und sonst nichts.

Wenn Sie Skripte schreiben, um wissenschaftliche Berechnungen durchzuführen, haben Sie wahrscheinlich Artikel mit den Gleichungen oder Algorithmen, die Sie verwenden. Wenn Sie neue Ideen verwenden, die Sie selbst entdeckt haben, werden Sie sie hoffentlich in eigenen Artikeln veröffentlichen. In diesem Fall lautet die Regel: Sie möchten, dass Ihr Code den veröffentlichten Gleichungen so gut wie möglich entspricht. Hier ist eine Antwort auf Software Engineering.SE mit über 200 positiven Stimmen, die sich für diesen Ansatz aussprechen und erklären, wie er aussieht: Gibt es eine Entschuldigung für kurze Variablennamen?

Als weiteres Beispiel gibt es in Simbody , einem Tool für die Physiksimulation, das für die Physikforschung und -entwicklung verwendet wird , einige großartige Codeausschnitte . Diese Ausschnitte enthalten einen Kommentar, der eine Gleichung zeigt, die für eine Berechnung verwendet wird, gefolgt von Code, der so genau wie möglich mit den implementierten Gleichungen übereinstimmt.

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 eins, um einen "Code zu erstellen, der den veröffentlichten Gleichungen so gut wie möglich entspricht". Sorry, Befürworter langer, aussagekräftiger Variablennamen. Die aussagekräftigsten Namen im wissenschaftlichen Code sind oft böse, kurz und brutal, gerade weil dies genau die Konvention ist, die in einer wissenschaftlichen Zeitschrift verwendet wird, die der Code zu implementieren versucht. Bei einem gleichungsreichen Codeblock, der Gleichungen aus einer Zeitschrift implementiert, ist es häufig am besten, möglichst nahe an der Nomenklatur in der Zeitschrift zu bleiben, und wenn dies gegen die guten Codierungsstandards verstößt, ist es schwierig.
David Hammen

@DavidHammen: Als Student respektiere ich das. Als Programmierer würde ich dann darauf bestehen, dass Sie über jeder Funktion einen riesigen Kommentarblock haben, der in einfachem Englisch (oder in einer Sprache Ihrer Wahl) beschreibt, wofür jede Variable steht, auch wenn es sich nur um einen temporären Platzhalter handelt. Auf diese Weise habe ich zumindest einen Hinweis, auf den ich zurückblicken kann.
Tonysdg

1
@ DavidHammen Außerdem Python-Unterstützung für UTF-8 in Quelldateien und einfache Regeln für Variablennamen macht es einfach zu deklarieren λoder φanstelle der hässlichen lambda_oder phy...
Mathias Ettinger

1
@tonysdg Sie haben bereits eine Referenz. es heißt "Hammen et al. (2018)" (oder was auch immer). Es wird die Bedeutung der Variablen detaillierter erklären, als es ein Kommentarblock jemals könnte. Der Grund dafür, dass die Variablennamen in der Nähe der Notation im Papier bleiben, ist genau, dass es einfacher ist, den Inhalt des Papiers mit dem Code zu verknüpfen.
Niemand

17

Mein Tagesjob ist also die Veröffentlichung und Speicherung von Forschungsdaten für das System der University of California. Einige Leute haben die Reproduzierbarkeit erwähnt, und ich denke, das ist wirklich das Kernproblem hier: Dokumentieren Sie Ihren Code so, wie Sie alles dokumentieren würden, was jemand zur Reproduktion Ihres Experiments benötigt, und schreiben Sie im Idealfall Code, der es für beide einfach macht um Ihr Experiment zu reproduzieren und Ihre Ergebnisse auf Fehlerquellen zu überprüfen.

Was ich jedoch noch nicht erwähnt habe und für wichtig halte, ist, dass Finanzierungsagenturen die Veröffentlichung von Software zunehmend als Teil der Datenpublikation betrachten und die Veröffentlichung von Software zu einer Voraussetzung für offene Wissenschaft machen.

Zu diesem Zweck kann ich die Software Carpentry- Organisation nicht genug empfehlen, wenn Sie etwas Bestimmtes wünschen, das sich eher an Forscher als an allgemeine Softwareentwickler richtet . Wenn Sie an einem ihrer Workshops teilnehmen können , ist das großartig. Wenn Sie nur Zeit haben, einige ihrer Artikel über bewährte Methoden für das wissenschaftliche Rechnen zu lesen , ist das auch gut. Aus letzterem:

Wissenschaftler entwickeln in der Regel ihre eigene Software für diese Zwecke, da hierfür umfangreiche domänenspezifische Kenntnisse erforderlich sind. Jüngste Studien haben ergeben, dass Wissenschaftler in der Regel 30% oder mehr ihrer Zeit mit der Entwicklung von Software verbringen. 90% oder mehr von ihnen sind jedoch in erster Linie Autodidakten und daher nicht in der Lage, grundlegende Softwareentwicklungsverfahren wie das Schreiben von wartbarem Code mithilfe von Versionskontrolle und Fehlerverfolgung, Codeüberprüfung, Komponententests und Aufgabenautomatisierung anzuwenden.

Wir glauben, dass Software nur eine andere Art von Versuchsapparatur ist und so sorgfältig wie jede physikalische Apparatur gebaut, geprüft und verwendet werden sollte. Während die meisten Wissenschaftler darauf achten, ihre Labor- und Feldgeräte zu validieren, wissen die meisten nicht, wie zuverlässig ihre Software ist. Dies kann zu schwerwiegenden Fehlern führen, die sich auf die zentralen Schlussfolgerungen der veröffentlichten Forschung auswirken. …

Da Software häufig für mehr als ein Projekt verwendet wird und häufig von anderen Wissenschaftlern wiederverwendet wird, können Rechenfehler unverhältnismäßige Auswirkungen auf den wissenschaftlichen Prozess haben. Diese Art von Kaskadeneffekt verursachte mehrere bedeutende Rückzüge, wenn ein Fehler aus dem Code einer anderen Gruppe erst nach der Veröffentlichung entdeckt wurde.

Ein allgemeiner Überblick über die von ihnen empfohlenen Praktiken:

  1. Schreiben Sie Programme für Menschen, nicht für Computer
  2. Lassen Sie den Computer die Arbeit machen
  3. Nehmen Sie inkrementelle Änderungen vor
  4. Wiederhole dich nicht (oder andere)
  5. Planen Sie Fehler ein
  6. Optimieren Sie die Software erst, wenn sie ordnungsgemäß funktioniert
  7. Dokumentendesign und Zweck, keine Mechanik
  8. Zusammenarbeiten

Das Papier geht auf jeden dieser Punkte sehr detailliert ein.


16

Ist es wirklich sinnvoll, Code zu schreiben, der als OOP bezeichnet wird, wenn einige Standardaufgaben hätte erledigt werden können, viel schneller zu schreiben gewesen wären und aufgrund der Kürze des Programms eine ähnliche Lesbarkeit gehabt hätten?

Persönliche Antwort:
Ich schreibe viel für wissenschaftliche Zwecke. Bei kleineren Skripten versuche ich einfach, allgemeine gute Programmierpraktiken zu befolgen (z. B. Versionskontrolle, Selbstkontrolle mit Variablennamen). Wenn ich nur etwas schreibe, um einen Datensatz schnell zu öffnen oder zu visualisieren, kümmere ich mich nicht um OOP.

Allgemeine Antwort:
"Es kommt darauf an." Wenn Sie jedoch Schwierigkeiten haben, herauszufinden, wann Sie ein Programmierkonzept oder -paradigma verwenden sollten, sollten Sie Folgendes bedenken:

  • Skalierbarkeit: Wird das Skript eigenständig oder wird es irgendwann in einem größeren Programm verwendet? Wenn ja, ist die größere Programmierung mit OOP? Kann der Code in Ihrem Skript einfach in das größere Programm integriert werden?
  • Modularität: Im Allgemeinen sollte Ihr Code modular sein. OOP zerlegt den Code jedoch auf ganz besondere Weise in Blöcke. Ist diese Art von Modularität (dh Aufteilen Ihres Skripts in Klassen) für Ihre Arbeit sinnvoll?

Ich möchte wissen, wie ich bei diesen kleineren, schnelleren Projekten von vorne anfangen und sauber programmieren kann.

# 1: Machen Sie sich mit den Neuerungen vertraut:
Auch wenn Sie "nur" Skripte schreiben (und sich wirklich nur für die Wissenschaftskomponente interessieren), sollten Sie sich etwas Zeit nehmen, um sich mit den verschiedenen Programmierkonzepten und -paradigmen vertraut zu machen . Auf diese Weise können Sie eine bessere Vorstellung davon haben, was und wann Sie verwenden sollten / sollten. Das klingt vielleicht ein bisschen entmutigend. Und Sie haben möglicherweise immer noch die Frage: "Wo fange ich an / was schaue ich mir an?" Ich versuche, einen guten Ausgangspunkt in den nächsten beiden Aufzählungspunkten zu erläutern.

# 2: Fange an zu reparieren, was du weißt, ist falsch:
Ich persönlich würde mit den Dingen beginnen, von denen ich weiß, dass sie falsch sind. Holen Sie sich etwas Versionskontrolle und fangen Sie an, sich zu disziplinieren, um mit diesen Variablennamen besser zu werden (es ist ein schwerer Kampf). Es mag offensichtlich klingen, wenn Sie wissen, dass das, was Sie wissen, falsch ist. Nach meiner Erfahrung habe ich jedoch festgestellt, dass das Reparieren einer Sache mich zu etwas anderem führt und so weiter. Bevor ich es weiß, habe ich 10 verschiedene Dinge enthüllt, die ich falsch gemacht habe, und herausgefunden, wie man sie behebt oder auf saubere Weise implementiert.

# 3: Holen Sie sich einen Programmierpartner:
Wenn Sie keinen formellen Unterricht absolvieren müssen, sollten Sie sich mit einem Entwickler zusammenschließen und ihn bitten, Ihren Code zu überprüfen. Auch wenn sie den wissenschaftlichen Teil Ihrer Arbeit nicht verstehen, können sie Ihnen möglicherweise sagen, was Sie getan haben könnten, um Ihren Code eleganter zu gestalten.

# 4: Konsortien
suchen : Ich weiß nicht, in welchem ​​wissenschaftlichen Bereich Sie tätig sind. Aber je nachdem, was Sie in der wissenschaftlichen Welt tun, suchen Sie nach Konsortien, Arbeitsgruppen oder Konferenzteilnehmern. Überprüfen Sie dann, ob Standards vorhanden sind, an denen gearbeitet wird. Das kann zu einigen Codierungsstandards führen. Zum Beispiel mache ich viel Geodatenarbeit. Der Blick auf Konferenzpapiere und Arbeitsgruppen führte mich zum Open Geospatial Consortium . Sie arbeiten unter anderem an Standards für die räumliche Entwicklung.

Ich hoffe das hilft!


Randnotiz: Ich weiß, Sie haben gerade OOP als Beispiel verwendet. Ich wollte nicht, dass du denkst, dass ich mich nur mit dem Schreiben von Code mit OOP beschäftige. Es war nur einfacher, eine Antwort zu schreiben, die dieses Beispiel fortsetzt.


Ich denke, # 3 ist das wichtigste Thema - ein erfahrener Programmierer kann dem OP die benötigten Konzepte mitteilen (# 1), wie man die Skripte besser organisiert und wie man die Versionskontrolle verwendet (# 2).
Doc Brown

16

Ich würde empfehlen, am Unix-Prinzip festzuhalten : Keep It Simple, Stupid! (KUSS)

Oder anders ausgedrückt: Machen Sie eine Sache nach der anderen und machen Sie es gut.

Was bedeutet das? Zunächst einmal bedeutet dies, dass Ihre Funktionen kurz sein sollten. Jede Funktion, deren Zweck, Verwendung und Implementierung innerhalb weniger Sekunden nicht vollständig verstanden werden kann, ist definitiv zu lang. Wahrscheinlich macht es mehrere Dinge gleichzeitig, von denen jede eine Funktion für sich sein sollte. Also spalte es auf.

In Bezug auf Codezeilen ist meine Heuristik, dass 10 Zeilen eine gute Funktion sind und alles, was über 20 hinausgeht, höchstwahrscheinlich Mist ist. Andere Leute haben andere Heuristiken. Das Wichtigste ist, die Länge so gering wie möglich zu halten.

Wie teilt man eine lange Funktion? Nun, zuerst suchen Sie nach sich wiederholenden Codemustern. Dann sind Sie ausklammern diese Codemuster, so dass sie einen beschreibenden Namen geben, und Ihr Code sehen schrumpfen . Das beste Refactoring ist das Refactoring, das die Codegröße reduziert.

Dies gilt insbesondere dann, wenn die betreffende Funktion mit Copy-Paste programmiert wurde. Immer wenn Sie ein solches wiederholtes Muster sehen, wissen Sie sofort, dass dies wahrscheinlich in eine eigene Funktion umgewandelt werden sollte. Dies ist das Prinzip von Don't Repeat Yourself (DRY) . Wann immer Sie Copy-Paste drücken, machen Sie etwas falsch! Erstellen Sie stattdessen eine Funktion.

Anekdote
Ich habe einmal mehrere Monate damit verbracht, Code zu überarbeiten, der Funktionen mit jeweils etwa 500 Zeilen hatte. Nachdem ich fertig war, war der Gesamtcode ungefähr tausend Zeilen kürzer; Ich hatte eine negative Ausgabe in Bezug auf Codezeilen produziert. Ich schuldete der Firma ( http://www.geekherocomic.com/2008/10/09/programmers-salary-policy/index.html ). Trotzdem glaube ich fest daran, dass dies eines meiner wertvollsten Werke war, das ich je gemacht habe ...

Einige Funktionen können lang sein, weil sie mehrere verschiedene Dinge nacheinander ausführen. Dies sind keine DRY-Verstöße, aber sie können auch aufgeteilt werden. Das Ergebnis ist häufig eine übergeordnete Funktion, die eine Reihe von Funktionen aufruft, die die einzelnen Schritte der ursprünglichen Funktionen implementieren. Dies erhöht im Allgemeinen die Codegröße, aber die hinzugefügten Funktionsnamen bewirken, dass der Code besser lesbar ist. Denn jetzt haben Sie eine Top-Level-Funktion mit allen explizit genannten Schritten. Nach dieser Aufteilung ist auch klar, welcher Schritt mit welchen Daten arbeitet. (Funktionsargumente. Sie verwenden keine globalen Variablen, oder?)

Eine gute Heuristik für diese Art der Aufteilung von Abschnittsfunktionen ist immer dann, wenn Sie versucht sind, einen Abschnittskommentar zu schreiben, oder wenn Sie einen Abschnittskommentar in Ihrem Code finden. Dies ist sehr wahrscheinlich einer der Punkte, an denen Ihre Funktion aufgeteilt werden sollte. Der Abschnittskommentar kann auch dazu dienen, einen Namen für die neue Funktion anzugeben.

Die Prinzipien von KISS und DRY können Sie weit bringen. Sie müssen nicht sofort mit OOP usw. beginnen. Oft können Sie große Vereinfachungen erzielen, indem Sie nur diese beiden anwenden. Langfristig lohnt es sich jedoch, sich mit OOP und anderen Paradigmen vertraut zu machen, da sie Ihnen zusätzliche Tools bieten, mit denen Sie Ihren Programmcode klarer gestalten können.

Notieren Sie abschließend jede Aktion mit einem Commit. Sie zerlegen etwas in eine neue Funktion, das ist ein Commit . Sie verschmelzen zwei Funktionen zu einer, weil sie wirklich dasselbe tun, das ist ein Commit . Wenn Sie eine Variable umbenennen , ist dies ein Commit . Legen Sie häufig fest. Wenn ein Tag vergeht und Sie sich nicht verpflichtet haben, haben Sie wahrscheinlich etwas falsch gemacht.


2
Tolle Punkte beim Aufteilen langer Methoden. Eine weitere gute Heuristik in Bezug auf den ersten Absatz nach der Anekdote: Wenn Ihre Methode logisch in Abschnitte unterteilt werden kann und Sie versucht sind, einen Kommentar zu schreiben, der erklärt, was jeder Abschnitt tut, sollte er bei den Kommentaren getrennt werden. Gute Nachrichten, diese Kommentare geben Ihnen wahrscheinlich eine gute Vorstellung davon, wie Sie die neuen Methoden aufrufen sollen.
Jaquez

@ Jaquez Ah, ich habe das total vergessen. Danke, dass du mich erinnert hast. Ich habe meine Antwort aktualisiert, um dies
einzuschließen

1
Tolle Punkte, ich möchte dies vereinfachen, um zu sagen, dass "TROCKEN" der wichtigste Einzelfaktor ist. Das Erkennen und Entfernen von "Wiederholungen" ist der Grundstein für fast alle anderen Programmierkonstruktionen. Anders ausgedrückt, alle Programmierkonstrukte sind zumindest teilweise dazu da, Ihnen bei der Erstellung von DRY-Code zu helfen. Sagen Sie zunächst "Keine Vervielfältigung" und üben Sie dann, diese zu identifizieren und zu beseitigen. Seien Sie sehr offen, was ein Duplikat sein könnte - auch wenn es kein ähnlicher Code ist, könnte es eine Duplizierungsfunktion sein ...
Bill K

11

Ich stimme den anderen zu, dass die Versionskontrolle viele Ihrer Probleme sofort lösen wird. Speziell:

  • Es ist nicht erforderlich, eine Liste der vorgenommenen Änderungen zu führen oder viele Kopien einer Datei usw. zu haben, da die Versionskontrolle sich darum kümmert.
  • Keine Dateien mehr, die durch Überschreiben usw. verloren gegangen sind (solange Sie sich nur an die Grundlagen halten; z. B. das "Überschreiben des Verlaufs" vermeiden)
  • Keine Notwendigkeit für veraltete Kommentare, toten Code usw., die "nur für den Fall" aufbewahrt werden müssen; Sobald sie sich der Versionskontrolle verschrieben haben, können Sie sie mit Nuklearwaffen versehen. Das kann sich sehr befreiend anfühlen!

Ich würde sagen, überdenke es nicht: benutze einfach git. Halten Sie sich an einfache Befehle (z. B. nur einen masterZweig), verwenden Sie möglicherweise eine grafische Benutzeroberfläche, und es sollte Ihnen gut gehen. Als Bonus kannst du gitlab, github usw. für kostenlose Veröffentlichungen und Backups verwenden;)

Der Grund, warum ich diese Antwort geschrieben habe, war, zwei Dinge anzusprechen, die Sie vielleicht versuchen, aber ich habe sie oben nicht gesehen. Die erste besteht darin, Behauptungen als einfache Alternative zu Unit-Tests zu verwenden. Unit-Tests tendieren dazu, sich "außerhalb" der Funktion / des Moduls / des zu testenden Objekts zu befinden: Sie senden normalerweise einige Daten an eine Funktion, empfangen ein Ergebnis zurück und überprüfen dann einige Eigenschaften dieses Ergebnisses. Dies ist im Allgemeinen eine gute Idee, kann jedoch aus folgenden Gründen unpraktisch sein (insbesondere, wenn Code weggeworfen werden soll):

  • Unit-Tests müssen entscheiden, welche Daten sie für die Funktion bereitstellen. Diese Daten müssen realistisch sein (ansonsten gibt es wenig Sinn, sie zu testen), sie müssen das richtige Format haben usw.
  • Unit-Tests müssen "Zugang" zu den Dingen haben, die sie behaupten wollen. Insbesondere können Unit-Tests keine der Zwischendaten in einer Funktion überprüfen. Wir müssten diese Funktion in kleinere Teile zerlegen, diese Teile testen und sie an einer anderen Stelle zusammenfügen.
  • Unit-Tests gelten ebenfalls als programmrelevant. Beispielsweise können Testsuiten "veraltet" werden, wenn seit ihrer letzten Ausführung große Änderungen vorgenommen wurden, und es kann sogar eine Reihe von Tests für Code geben, der nicht mehr verwendet wird.

Behauptungen haben diese Nachteile nicht, da sie während der normalen Ausführung eines Programms überprüft werden. Speziell:

  • Da sie als Teil der normalen Programmausführung ausgeführt werden, haben wir echte Daten, mit denen wir spielen können. Dies erfordert keine separate Kuration und ist (per Definition) realistisch und hat das richtige Format.
  • Zusicherungen können an einer beliebigen Stelle im Code geschrieben werden, sodass wir sie überall dort ablegen können, wo wir Zugriff auf die zu überprüfenden Daten haben. Wenn wir einen Zwischenwert in einer Funktion testen wollen, können wir einfach einige Aussagen in die Mitte dieser Funktion setzen!
  • Da sie inline geschrieben sind, können Assertions nicht mit der Struktur des Codes synchronisiert werden. Wenn wir sicherstellen, dass die Zusicherungen standardmäßig aktiviert sind, brauchen wir uns auch keine Sorgen zu machen, dass sie "abgestanden" werden, da wir sofort sehen, ob sie beim nächsten Ausführen des Programms bestehen oder nicht!

Sie erwähnen die Geschwindigkeit als einen Faktor. In diesem Fall ist die Überprüfung von Zusicherungen in dieser Schleife möglicherweise unerwünscht (aber dennoch nützlich, um die Einrichtung und die nachfolgende Verarbeitung zu überprüfen). Fast alle Implementierungen von Zusicherungen bieten jedoch eine Möglichkeit, sie zu deaktivieren. Zum Beispiel können sie in Python anscheinend durch Ausführen mit der -OOption deaktiviert werden (das wusste ich nicht, da ich noch nie das Bedürfnis hatte, eine meiner Behauptungen zu deaktivieren). Ich würde empfehlen, dass Sie sie auf lassenstandardmäßig; Wenn sich Ihr Codierungs- / Debugging- / Testzyklus verlangsamt, ist es möglicherweise besser, mit einer kleineren Teilmenge Ihrer Daten zu testen oder während des Testens weniger Iterationen einer Simulation durchzuführen oder was auch immer. Wenn Sie Behauptungen in Nicht-Testläufen aus Leistungsgründen deaktivieren, ist das erste, was ich empfehle, zu messen, ob sie tatsächlich die Ursache für die Verlangsamung sind! (Es ist sehr leicht, sich zu täuschen, wenn es um Leistungsengpässe geht)

Mein letzter Ratschlag wäre, ein Build-System zu verwenden, das Ihre Abhängigkeiten verwaltet. Ich persönlich benutze Nix dafür, habe aber auch gute Dinge über Guix gehört . Es gibt auch Alternativen wie Docker, die aus wissenschaftlicher Sicht weitaus weniger nützlich, aber vielleicht ein wenig vertrauter sind.

Systeme wie Nix werden erst seit kurzem (ein wenig) populär, und manche mögen sie für übertrieben halten, um Code, wie Sie ihn beschreiben, "wegzuwerfen", aber ihr Nutzen für die Reproduzierbarkeit des wissenschaftlichen Rechnens ist enorm. Betrachten Sie ein Shell-Skript zum Ausführen eines Experiments wie dieses (zB run.sh):

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

Wir können es stattdessen wie folgt in eine Nix-Ableitung umschreiben run.nix:

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

Das Zeug dazwischen ''...''ist Bash-Code, derselbe wie zuvor, mit der Ausnahme, dass er ${...}verwendet werden kann, um den Inhalt anderer Zeichenfolgen zu "spleißen" (in diesem Fall ./.wird er auf den Pfad des Verzeichnisses erweitert, das ihn enthält run.nix). Die with import ...Zeile importiert die Standardbibliothek von Nix , in der runCommandBash-Code ausgeführt werden kann. Wir können unser Experiment unter Verwendung von ausführen nix-build run.nix, wodurch sich ein Pfad wie ergibt /nix/store/1wv437qdjg6j171gjanj5fvg5kxc828p-output.csv.

Was bringt uns das? Nix wird automatisch eine "saubere" Umgebung einrichten, die nur Zugriff auf Dinge hat, nach denen wir ausdrücklich gefragt haben. Insbesondere hat es keinen Zugriff auf Variablen wie $HOMEoder eine von uns installierte Systemsoftware. Dies macht das Ergebnis unabhängig von den Details unserer aktuellen Maschine, wie dem Inhalt ~/.configoder den Versionen von Programmen, die wir zufällig installiert haben. AKA das Zeug, das andere Leute davon abhält, unsere Ergebnisse zu replizieren! Deshalb habe ich das hinzugefügtcpBefehl, da das Projekt standardmäßig nicht zugänglich ist. Es mag ärgerlich erscheinen, dass die Software des Systems für ein Nix-Skript nicht verfügbar ist, aber es geht auch in die andere Richtung: Auf unserem System muss nichts (außer Nix) installiert sein, um sie in einem Skript verwenden zu können. wir fragen einfach danach und Nix wird loslegen und alles Nötige holen / kompilieren / was auch immer nötig ist (die meisten Dinge werden als Binärdateien heruntergeladen; die Standardbibliothek ist auch riesig!). Wenn wir zum Beispiel eine Reihe bestimmter Python- und Haskell-Pakete für einige bestimmte Versionen dieser Sprachen und einige andere Junk-Dateien benötigen (warum nicht?):

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
  ''

Der selbe nix-build run.nixwird dies ausführen und alles holen, wonach wir zuerst gefragt haben (und alles zwischenspeichern, falls wir es später wollen). Die Ausgabe (jede Datei / jedes Verzeichnis, das aufgerufen wird $out) wird von Nix gespeichert. Dies ist der Pfad, den es ausgibt. Es wird durch den kryptografischen Hash aller von uns angeforderten Eingaben identifiziert (Skriptinhalte, andere Pakete, Namen, Compiler-Flags usw.). Diese anderen Pakete werden durch Hashes ihrer Eingaben usw. identifiziert , sodass wir eine vollständige Kette von Provinenzen für alles haben, zurück zu der Version von GCC, die die Version von GCC kompiliert hat, die die Bash kompiliert hat, und so weiter!

Hoffentlich habe ich gezeigt, dass dies uns viel für wissenschaftlichen Code einkauft und einigermaßen einfach ist, damit anzufangen. Es wird auch von Wissenschaftlern sehr ernst genommen, zB (Top-Google-Hit) https://dl.acm.org/citation.cfm?id=2830172. Dies könnte eine wertvolle Fähigkeit sein, die man kultivieren kann (genau wie das Programmieren).


2
Sehr detaillierte nützliche Antwort - Ich mag die anderen Antworten wirklich, aber die Behauptungen klingen nach einem sehr nützlichen ersten Schritt.
Heather

9

Ohne auf die vollständige Versionskontrolle + Verpackung + Einheitentests zurückgreifen zu müssen (das sind gute Programmierpraktiken, die Sie irgendwann versuchen sollten), besteht eine meiner Meinung nach passende Zwischenlösung in der Verwendung von Jupiter Notebook . Dies scheint sich besser in die wissenschaftliche Berechnung integrieren zu lassen.

Es hat den Vorteil, dass Sie Ihre Gedanken mit dem Code mischen können; Erklären, warum ein Ansatz besser ist als ein anderer, und Belassen des alten Codes in einem Ad-hoc-Abschnitt. Neben der richtigen Verwendung von Zellen werden Sie natürlich dazu gebracht, Ihren Code zu fragmentieren und in Funktionen zu organisieren, die das Verständnis erleichtern.


1
Dies hilft auch bei der Reproduzierbarkeit. Sie können beispielsweise genau denselben Code ausführen, um eine Publikationszahl zu generieren, oder um zu etwas zurückzukehren, das Sie vor Monaten beiseite gelegt haben, um möglicherweise Kommentare von Überprüfern zu berücksichtigen.
Afaulconbridge

Für jemanden, der mehr lesen möchte, wird dies auch als Lese- und Schreibprogrammierung bezeichnet.
llrs

6

Die besten Antworten sind bereits gut, aber ich wollte einige Ihrer Fragen direkt beantworten.

Ist ein Komponententest erforderlich, um kleinere Codeteile zu schreiben?

Die Größe des Codes steht nicht in direktem Zusammenhang mit der Notwendigkeit von Komponententests. Dies hängt indirekt zusammen: In komplexen Codebasen sind Komponententests wertvoller , und kleine Codebasen sind im Allgemeinen nicht so komplex wie größere.

Unit-Tests überzeugen durch Code, bei dem es leicht ist, Fehler zu machen, oder wenn Sie viele Implementierungen dieses Codes haben werden. Unit-Tests helfen Ihnen wenig bei der aktuellen Entwicklung, verhindern jedoch, dass Sie in Zukunft Fehler machen, die dazu führen, dass sich vorhandener Code plötzlich schlecht verhält (obwohl Sie das Ding nicht angerührt haben).

Angenommen, Sie haben eine Anwendung, in der Bibliothek A das Quadrieren von Zahlen ausführt und Bibliothek B den Satz von Pythagoras anwendet. Offensichtlich hängt B von A ab. Sie müssen etwas in Bibliothek A korrigieren. Nehmen wir an, Sie führen einen Fehler ein, der Zahlen würfelt, anstatt sie zu quadrieren.

Bibliothek B verhält sich plötzlich schlecht und löst möglicherweise Ausnahmen aus oder gibt einfach eine falsche Ausgabe aus. Und wenn Sie sich die Versionshistorie von Bibliothek B ansehen, sehen Sie, dass sie unberührt bleibt. Das problematische Endergebnis ist, dass Sie keinen Hinweis darauf haben, was schief gehen könnte, und dass Sie das Verhalten von B debuggen müssen, bevor Sie erkennen, dass das Problem in A liegt. Das ist vergeblicher Aufwand.

Komponententests eingeben. Diese Tests bestätigen, dass Bibliothek A wie beabsichtigt funktioniert. Wenn Sie einen Fehler in Bibliothek A einführen, der zu schlechten Ergebnissen führt, werden Ihre Komponententests dies feststellen. Daher müssen Sie nicht länger versuchen, Bibliothek B zu debuggen.
Dies liegt außerhalb Ihres Bereichs. In einer kontinuierlichen Integrationsentwicklung werden Komponententests jedoch immer dann ausgeführt, wenn jemand Code festschreibt.

Insbesondere für komplizierte mathematische Operationen können Unit-Tests ein Segen sein. Sie führen einige Beispielberechnungen durch und schreiben dann Einheitentests, in denen Ihre berechnete Ausgabe und Ihre tatsächliche Ausgabe (basierend auf denselben Eingabeparametern) verglichen werden.

Beachten Sie jedoch, dass Sie mit Unit-Tests keinen guten Code erstellen , sondern ihn beibehalten können. Wenn Sie Code normalerweise einmal schreiben und ihn nie wieder aufrufen, sind Komponententests weniger vorteilhaft.

Wie wäre es mit OOP?

OOP ist eine Denkweise über verschiedene Entitäten, zum Beispiel:

Wenn ein Customereinkaufen möchte Product, spricht er mit dem Vendor, um ein zu erhalten Order. Der Accountantzahlt dann die Vendor.

Vergleichen Sie dies damit, wie ein funktionaler Programmierer über Dinge denkt:

Wenn ein Kunde es will, purchaseProduct()wird er es ihm talktoVendor()so tun sendOrder(). Der Buchhalter wird dann payVendor().

Äpfel und Orangen. Keiner von ihnen ist objektiv besser als der andere. Eine interessante Sache zu beachten ist, dass für OOP, Vendorwird zweimal erwähnt, aber es bezieht sich auf die gleiche Sache. Doch für die funktionale Programmierung, talktoVendor()und payVendor()sind zwei verschiedene Dinge.
Dies zeigt den Unterschied zwischen den Ansätzen. Wenn zwischen diesen beiden Aktionen viele herstellerspezifische Gemeinsamkeiten bestehen, trägt OOP zur Reduzierung der Codeduplizierung bei. Wenn es jedoch keine gemeinsame Logik zwischen den beiden gibt, ist das Zusammenführen zu einer einzigen Vendorvergeblich (und daher ist funktionale Programmierung effizienter).

In den meisten Fällen sind mathematische und wissenschaftliche Berechnungen unterschiedliche Operationen, die nicht auf impliziter gemeinsamer Logik / Formel beruhen. Aus diesem Grund wird funktionale Programmierung häufiger verwendet als OOP.

Welche Ansätze eignen sich, um bei "wissenschaftlichem Programmieren" guten, sauberen Code schnell zu schreiben, anstatt an größeren Projekten zu arbeiten?

Ihre Frage impliziert, dass sich die Definition von "gutem, sauberem Code" ändert, unabhängig davon, ob Sie wissenschaftliche Programme ausführen oder an größeren Projekten (ich nehme an, Sie meinen Unternehmensprojekte) arbeiten.

Die Definition von gutem Code ändert sich nicht. Die Notwendigkeit, Komplexität zu vermeiden (was durch das Schreiben von sauberem Code erreicht werden kann), ändert sich jedoch.

Das gleiche Argument kommt hier zurück.

  • Wenn Sie alten Code nie wieder aufrufen und die Logik vollständig verstehen, ohne sie unterteilen zu müssen, müssen Sie sich nicht übermäßig anstrengen, um die Wartung zu ermöglichen.
  • Wenn Sie alten Code überarbeiten oder die erforderliche Logik zu komplex ist, um sie auf einmal anzugehen (und Sie daher die Lösungen unterteilen müssen), konzentrieren Sie sich darauf, einen sauberen, wiederverwendbaren Abschluss zu schreiben.

Ich stelle diese Fragen, weil die Programmierung selbst oft nicht sehr komplex ist. Es geht eher um Mathematik oder Naturwissenschaften, die ich mit der Programmierung teste oder erforsche.

Ich verstehe die Unterscheidung, die Sie hier treffen, aber wenn Sie auf vorhandenen Code zurückblicken , betrachten Sie sowohl die Mathematik als auch die Programmierung. Wenn entweder erfunden oder komplex ist, werden Sie Schwierigkeiten haben, es zu lesen.

Ist zB eine Klasse notwendig, wenn zwei Variablen und eine Funktion sich wahrscheinlich darum kümmern könnten?

Abgesehen von den OOP-Prinzipien ist der Hauptgrund, warum ich Klassen schreibe, um einige Datenwerte aufzunehmen, die Vereinfachung der Deklaration von Methodenparametern und Rückgabewerten. Wenn ich zum Beispiel viele Methoden habe, die einen Ort verwenden (Lat / Lon-Paar), werde ich schnell müde zu tippen float latitude, float longitudeund schreibe viel lieber Location loc.

Dies wird noch weiter verschärft, wenn Sie berücksichtigen, dass Methoden im Allgemeinen einen Wert zurückgeben (es sei denn, es gibt sprachspezifische Features, um mehr Werte zurückzugeben), und wenn Sie beispielsweise bei einem Standort zwei Werte zurückgeben möchten (lat + lon). Dies ist ein Anreiz für Sie, eine LocationKlasse zu erstellen , um Ihren Code zu vereinfachen.

Ist zB eine Klasse notwendig, wenn zwei Variablen und eine Funktion sich wahrscheinlich darum kümmern könnten?

Eine weitere interessante Sache ist, dass Sie OOP verwenden können, ohne Datenwerte und Methoden zu mischen. Nicht jeder Entwickler stimmt dem zu (manche nennen es ein Antipattern), aber Sie können anämische Datenmodelle haben, in denen Sie separate Datenklassen (speichert Wertfelder) und Logikklassen (speichert Methoden) haben.
Dies ist natürlich ein Spektrum. Sie müssen nicht perfekt anämisch sein, Sie können es verwenden, wenn Sie es für angemessen halten.

Zum Beispiel kann eine Methode, die einfach den Vor- und Nachnamen einer Person verkettet, immer noch in der PersonKlasse selbst enthalten sein, da es sich nicht wirklich um eine "Logik" handelt, sondern um einen berechneten Wert.

(Bedenken Sie, dass dies im Allgemeinen auch Situationen sind, in denen die Geschwindigkeit des Programms am schnellsten ist. Wenn Sie mehr als 25.000.000 Zeitschritte einer Simulation ausführen, möchten Sie, dass dies der Fall ist.)

Eine Klasse ist immer so groß wie die Summe ihrer Felder. Am Beispiel von Locationagain, das aus zwei floatWerten besteht, ist zu beachten, dass ein einzelnes LocationObjekt genauso viel Speicherplatz beansprucht wie zwei separate floatWerte.

In diesem Sinne spielt es keine Rolle, ob Sie OOP verwenden oder nicht. Der Speicherbedarf ist der gleiche.

Die Performance selbst ist auch keine große Hürde zu überwinden. Der Unterschied zwischen der Verwendung einer globalen Methode oder einer Klassenmethode hat nichts mit der Laufzeitleistung zu tun, sondern mit der Generierung von Bytecode zur Kompilierungszeit.

Stellen Sie sich das so vor: Ob ich mein Kuchenrezept auf Englisch oder Spanisch schreibe, ändert nichts an der Tatsache, dass das Backen des Kuchens 30 Minuten dauert (= Laufzeitleistung). Die Sprache des Rezepts ändert sich nur dadurch, wie der Koch die Zutaten mischt (= den Bytecode zusammenstellt).

Für Python müssen Sie den Code nicht explizit vorkompilieren, bevor Sie ihn aufrufen. Wenn Sie jedoch nicht vorkompilieren, wird die Kompilierung ausgeführt, wenn Sie versuchen, den Code auszuführen. Wenn ich "Laufzeit" sage, meine ich die Ausführung selbst, nicht die Kompilierung, die der Ausführung vorausgehen könnte.


6

Vorteile eines sauberen wissenschaftlichen Codes

  • ... bei der Betrachtung von Programmbüchern scheinen sie häufig bei größeren Projekten angesprochen zu werden.

  • ... ist es wirklich sinnvoll, Code zu schreiben, der besagt, dass OOP möglich gewesen wäre, wenn einige Standardaufgaben erledigt worden wären, viel schneller zu schreiben gewesen wären und aufgrund der Kürze des Programms eine ähnliche Lesbarkeit gehabt hätten?

Es kann hilfreich sein, Ihren Code aus der Perspektive eines zukünftigen Codierers zu betrachten.

  • Warum haben sie diese Datei geöffnet?
  • Wonach suchen sie?

Meiner Erfahrung nach,

Sauberer Code sollte es einfach machen, Ihre Ergebnisse zu überprüfen

  • Machen Sie es den Benutzern einfach, genau zu wissen, was sie zum Ausführen Ihres Programms tun müssen.
  • Möglicherweise möchten Sie Ihr Programm so aufteilen, dass einzelne Algorithmen separat verglichen werden können.

  • Vermeiden Sie das Schreiben von Funktionen mit kontraintuitiven Nebeneffekten, bei denen ein Vorgang ohne Beziehung zu einem anderen Vorgang führt, der sich anders verhält. Wenn Sie es nicht vermeiden können, dokumentieren Sie, was Ihr Code benötigt und wie Sie ihn einrichten.

Clean Code kann als Beispielcode für zukünftige Codierer dienen

Klare Kommentare (einschließlich solcher, die zeigen, wie Funktionen aufgerufen werden sollten) und gut getrennte Funktionen können einen großen Unterschied darin ausmachen, wie lange es dauert, bis jemand anfängt (oder Sie zukünftig), etwas Nützliches aus Ihrer Arbeit zu machen.

Darüber hinaus können Sie sich durch die Erstellung einer echten "API" für Ihren Algorithmus besser vorbereiten, wenn Sie Ihre Skripte in eine echte Bibliothek für andere Benutzer umwandeln möchten.

Empfehlungen

Mathematische Formeln mit Kommentaren "zitieren".

  • Fügen Sie Kommentare hinzu, um mathematische Formeln zu zitieren, insbesondere wenn Sie Optimierungen verwendet haben (Trigger-Identitäten, Taylor-Reihen usw.).
  • Wenn Sie die Formel aus dem Buch erhalten haben, fügen Sie einen Kommentar hinzu, der besagt John Smith Method from Some Book 1st Ed. Section 1.2.3 Pg 180, dass Sie die Formel auch auf einer Website oder in einer Zeitung gefunden haben.
  • Ich würde empfehlen, "Nur verlinken" -Kommentare zu vermeiden. Vergewissern Sie sich, dass Sie sich irgendwo mit dem Namen der Methode befassen , damit die Leute sie googeln können. Ich habe einige "Nur verlinken" -Kommentare gefunden, die auf alte interne Seiten umgeleitet wurden und die sehr frustrierend sein können .
  • Sie können versuchen, die Formel in Ihren Kommentar einzutippen, wenn Unicode / ASCII noch gut lesbar ist, dies kann jedoch sehr umständlich sein (Codekommentare sind nicht LaTeX).

Verwenden Sie Kommentare mit Bedacht

Wenn Sie die Lesbarkeit Ihres Codes verbessern können, indem Sie gute Variablennamen / Funktionsnamen verwenden, tun Sie dies zuerst. Denken Sie daran, dass Kommentare für immer erhalten bleiben, bis Sie sie entfernen. Versuchen Sie daher, Kommentare zu verfassen, die nicht veraltet sind.

Verwenden Sie beschreibende Variablennamen

  • Variablen mit einem Buchstaben sind möglicherweise die beste Option, wenn sie Teil einer Formel sind.
  • Für zukünftige Leser kann es von entscheidender Bedeutung sein, den von Ihnen geschriebenen Code anzusehen und ihn mit der von Ihnen implementierten Gleichung zu vergleichen.
  • Fügen Sie gegebenenfalls ein Suffix hinzu, um die tatsächliche Bedeutung zu beschreiben, z. xBar_AverageVelocity
  • Wie bereits erwähnt, empfehle ich, die Formel / Methode, die Sie verwenden, in einem Kommentar eindeutig anzugeben.

Schreiben Sie Code, um Ihr Programm gegen bekannte gute und bekannte schlechte Daten auszuführen.

Ist ein Komponententest erforderlich, um kleinere Codeteile zu schreiben?

Ich denke, Unit-Tests können hilfreich sein. Ich denke, die beste Form von Unit-Tests für wissenschaftlichen Code sind eine Reihe von Tests, die mit bekannten schlechten und guten Daten durchgeführt werden.

Schreiben Sie Code, um Ihren Algorithmus auszuführen, und überprüfen Sie, inwieweit das Ergebnis von Ihren Erwartungen abweicht. Dies hilft Ihnen, Probleme zu finden (möglicherweise sehr schlimm und schwer zu finden), bei denen Sie versehentlich etwas hart codieren, was zu einem falsch positiven Ergebnis führt, oder einen Fehler machen, der dazu führt, dass die Funktion immer den gleichen Wert zurückgibt.

Beachten Sie, dass dies auf jeder Abstraktionsebene möglich ist. Sie können beispielsweise einen gesamten Pattern-Matching-Algorithmus oder eine Funktion testen, die nur den Abstand zwischen zwei Ergebnissen in Ihrem Optimierungsprozess berechnet. Beginnen Sie mit den Bereichen, die für Ihre Ergebnisse am wichtigsten sind, und / oder mit den Teilen des Codes, die Sie am meisten interessieren.

Erleichtern Sie das Hinzufügen neuer Testfälle, erwägen Sie das Hinzufügen von "Hilfsfunktionen" und strukturieren Sie Ihre Eingabedaten effektiv. Dies kann bedeuten, dass möglicherweise Eingabedaten in einer Datei gespeichert werden, damit Sie die Tests problemlos wiederholen können. Achten Sie jedoch darauf, dass Sie keine falsch positiven oder voreingenommenen / trivial gelösten Testfälle erhalten.

Erwägen Sie die Verwendung einer Kreuzvalidierung. Weitere Informationen finden Sie in diesem Beitrag zur Kreuzvalidierung .

Verwenden Sie die Versionskontrolle

Ich würde empfehlen, die Versionskontrolle zu verwenden und Ihr Repository auf einer externen Site zu hosten. Es gibt Websites, auf denen Repositories kostenlos gehostet werden.

Vorteile:

  1. Es bietet eine Sicherung für den Fall, dass Ihre Festplatte ausfällt
  2. Es enthält einen Verlauf, mit dem Sie sich keine Sorgen machen können, ob ein kürzlich aufgetretenes Problem unter anderem dadurch verursacht wurde, dass Sie eine Datei versehentlich geändert haben.
  3. Hiermit können Sie Verzweigungen verwenden. Dies ist eine gute Möglichkeit, um an Langzeit- / experimentellem Code zu arbeiten, ohne dass dies Auswirkungen auf die nicht zusammenhängende Arbeit hat.

Seien Sie vorsichtig beim Kopieren / Einfügen von Code

Der Stil meines Codes ist verworren und mit veralteten Kommentaren gefüllt, die alternative Methoden zur Ausführung von Vorgängen angeben, oder mit Codezeilen, die überschrieben werden.

  • Das Kopieren / Einfügen von Code kann Ihnen Zeit sparen, ist jedoch eine der gefährlichsten Aktionen, die Sie ausführen können, insbesondere wenn es sich um Code handelt, den Sie nicht selbst geschrieben haben (z. B. wenn es sich um Code eines Kollegen handelt).

  • Sobald Sie den Code zum Laufen gebracht und getestet haben, empfehle ich Ihnen, ihn sorgfältig durchzuarbeiten, um alle Variablen umzubenennen oder etwas zu kommentieren, was Sie nicht verstehen.



6

Die Werkzeuge des Handels sind in der Regel erfunden, um ein Bedürfnis zu lösen. Wenn Sie die Notwendigkeit haben, verwenden Sie das Tool, wenn nicht, müssen Sie höchstwahrscheinlich nicht.

Insbesondere sind wissenschaftliche Programme nicht das Ziel, sondern das Mittel. Sie schreiben das Programm, um ein Problem zu lösen, das Sie jetzt haben - Sie erwarten nicht, dass dieses Programm in zehn Jahren von anderen verwendet wird (und gewartet werden muss). Das allein bedeutet , dass Sie nicht haben , in eines des Werkzeug zu denken , dass die aktuellen Entwickler aufzeichnen Geschichte für andere wie Versionskontrolle, oder Capture - Funktionalität in Code wie Unit - Tests ermöglichen.

Was würde dir dann nützen?

  • Versionskontrolle ist nett, weil es Ihnen erlaubt, Ihre Arbeit sehr leicht zu sichern. Ab 2018 ist Github ein sehr beliebter Ort, um dies zu tun (und Sie können es jederzeit später verschieben, falls erforderlich - GIT ist sehr flexibel). Ein billiger und einfacher Ersatz für Backups sind die automatischen Backup-Vorgänge in Ihrem Betriebssystem (Time Machine für Mac, rsync für Linux usw.). Ihr Code muss sich an mehreren Stellen befinden!
  • Unit-Tests sind nett, denn wenn Sie sie zuerst schreiben, müssen Sie sich überlegen, wie Sie die tatsächliche Funktion des Codes überprüfen können. Auf diese Weise können Sie eine nützlichere API für Ihren Code entwickeln. Dies ist hilfreich, wenn Sie Code schreiben, der später wiederverwendet werden soll, und hilft Ihnen beim Ändern eines Algorithmus, da Sie wissen, dass er in diesen Fällen funktioniert.
  • Dokumentation. Erfahren Sie, wie Sie in der von Ihnen verwendeten Programmiersprache (z. B. Javadoc für Java) die richtige Dokumentation schreiben. Schreibe für die Zukunft du. In diesem Prozess werden Sie feststellen, dass gute Variablennamen die Dokumentation erleichtern. Iteriere. Schenken Sie Ihrer Dokumentation die gleiche Sorgfalt, die ein Dichter für Gedichte verwendet.
  • Verwenden Sie gute Werkzeuge. Finden Sie eine IDE, die Ihnen hilft und lernen Sie es gut. Das Umgestalten wie das Umbenennen von Variablen in einen besseren Namen ist auf diese Weise viel einfacher.
  • Wenn Sie Peers haben, ziehen Sie die Verwendung von Peer Review in Betracht. Wenn ein Außenstehender Ihren Code betrachtet und versteht, ist dies die aktuelle Version der Zukunft, für die Sie schreiben. Wenn Ihr Peer Ihren Code nicht versteht, werden Sie es wahrscheinlich auch nicht später tun.

Wie hat diese Antwort keine positive Bewertung erhalten? Es hat jetzt. Unsere Gruppe hat festgestellt, dass Peer Review eines der effektivsten Werkzeuge von allen ist, was in Bezug auf wissenschaftlichen Code viel wichtiger ist als Unit-Tests. Es ist leicht, einen Fehler zu machen, wenn ein komplexer Satz von Gleichungen in einem wissenschaftlichen Zeitschriftenartikel in Code übersetzt wird. Wissenschaftler und Ingenieure sorgen oft für extrem arme Programmierer. Peer Review kann architektonische Hässlichkeiten aufdecken, die die Pflege / das Verständnis / die Verwendung des Codes erschweren.
David Hammen

5

Zusätzlich zu den guten Ratschlägen hier möchten Sie vielleicht den Zweck Ihrer Programmierung und damit das berücksichtigen, was für Sie wichtig ist.

"Es geht mehr um Mathematik oder Naturwissenschaften, die ich mit der Programmierung teste oder erforsche."

Wenn der Zweck darin besteht, etwas zu experimentieren und zu testen, um Ihr eigenes Verständnis zu erlangen, und Sie wissen, was die Ergebnisse sein sollten, ist Ihr Code im Grunde genommen ein schneller Wegwerfvorgang, und Ihre derzeitige Vorgehensweise könnte ausreichen, könnte jedoch verbessert werden. Wenn die Ergebnisse nicht den Erwartungen entsprechen, können Sie sie erneut überprüfen.

Wenn die Ergebnisse Ihrer Codierung jedoch die Richtung Ihrer Forschung angeben und Sie nicht wissen, wie die Ergebnisse aussehen sollen, ist die Richtigkeit besonders wichtig. Ein Fehler in Ihrem Code könnte dazu führen, dass Sie aus Ihrem Experiment die falschen Schlussfolgerungen ziehen und eine Reihe von negativen Auswirkungen auf Ihre Gesamtrecherche haben.

In diesem Fall erhalten Sie durch Aufteilen Ihres Codes in leicht verständliche und überprüfbare Funktionen mit Komponententests stabilere Bausteine, die Ihnen mehr Vertrauen in Ihre Ergebnisse geben und Sie möglicherweise später vor viel Frust bewahren.


5

So wichtig Versionskontrolle und Komponententests auch sind, um den gesamten Code organisiert und funktionsfähig zu halten, sie helfen Ihnen auch nicht, saubereren Code zu schreiben.

  • Mithilfe der Versionskontrolle können Sie sehen, wie und wann der Code so unordentlich geworden ist, wie er ist.
  • Unit-Tests stellen sicher, dass der Code auch dann noch funktioniert, wenn er ein komplettes Durcheinander ist.

Wenn Sie sich davon abhalten möchten, unordentlichen Code zu schreiben, benötigen Sie ein Tool, das dort funktioniert, wo die Probleme auftreten: beim Schreiben des Codes. Eine beliebte Art von Werkzeug ist ein Linter. Ich bin kein Python-Entwickler, aber es sieht so aus, als wäre Pylint eine gute Option.

Ein Linter überprüft den von Ihnen geschriebenen Code und vergleicht ihn mit einer konfigurierbaren Reihe von Best Practices. Wenn der Linter die Regel hat, dass Variablen sein müssen camelCase, und Sie eine snake_caseeingeben, wird dies als Fehler markiert. Gute Linters haben Regeln von "deklarierte Variablen müssen verwendet werden" bis "Die zyklomatische Komplexität von Funktionen muss kleiner als 3 sein".

Die meisten Code-Editoren können so konfiguriert werden, dass bei jedem Speichern oder nur während der Eingabe ein Linter ausgeführt wird und Probleme inline angezeigt werden. Wenn Sie etwas wie eingeben x = 7, xwird das hervorgehoben, mit der Anweisung, einen längeren, besseren Namen zu verwenden (sofern Sie dies konfiguriert haben). Dies funktioniert wie die Rechtschreibprüfung in den meisten Textverarbeitungsprogrammen, was das Ignorieren erschwert und zu besseren Gewohnheiten beiträgt.


Dies sollte mehr positive Stimmen haben. +1
Heather

2
Aber, um Himmels willen, stellen Sie sicher, dass Sie wissen, wie Sie den Linter auf einen Stil einstellen, den Sie mögen, sonst werden Sie verrückt vor Aufregung.
DrMcCleod

4

Alles, was Sie aufgelistet haben, ist ein Werkzeug in der metaphorischen Toolbox. Wie alles im Leben sind verschiedene Werkzeuge für verschiedene Aufgaben geeignet.

Im Vergleich zu anderen technischen Bereichen arbeitet Software mit einer Reihe von Einzelteilen, die für sich genommen recht einfach sind. Eine Zuweisungsanweisung wird in Abhängigkeit von Temperaturschwankungen des Raumes nicht unterschiedlich bewertet. Eine ifAussage korrodiert nicht und gibt nach einer Weile immer wieder dasselbe zurück. Aber weil die einzelnen Elemente so einfach und von Menschen erstellte Software sind, werden diese Elemente zu immer größeren Teilen kombiniert, bis das Ergebnis so umfangreich und komplex wird, dass es an die Grenzen dessen stößt, was Menschen mental verwalten können.

Mit dem Anwachsen von Softwareprojekten haben die Menschen diese untersucht und Tools entwickelt, um diese Komplexität zu bewältigen. OOP ist ein Beispiel. Immer abstrakter werdende Programmiersprachen sind ein weiteres Mittel. Da ein Großteil des Geldes in Software mehr und mehr fließt, werden Sie Tools dafür sehen und lesen. Aber es scheint, dass diese Situationen nicht auf Sie zutreffen.

Also, nicht das Gefühl , wie Sie brauchen irgendetwas davon zu tun werden. Letztendlich ist der Code nur ein Mittel zum Zweck. Leider ist es für Sie am besten, an einigen größeren Projekten zu arbeiten, da es sehr viel schwieriger ist zu wissen, was fehlt, wenn Sie an der Toolbox interessiert sind.

In jedem Fall würde ich mir keine Sorgen machen, wenn ich kein OOP oder andere Techniken verwende, solange Ihre Skripte klein sind. Viele der Probleme, die Sie beschrieben haben, sind nur allgemeine berufliche Organisationsfähigkeiten. Das heißt, nicht eine alte Datei zu verlieren, ist etwas, mit dem sich alle Bereiche befassen müssen.


4

Zusätzlich zu all den guten Vorschlägen, die ich bisher gemacht habe, ist es eine Übung, die ich im Laufe der Zeit gelernt habe und die ich für wesentlich halte, Ihrem Code sehr großzügig detaillierte Kommentare hinzuzufügen. Es ist das Wichtigste für mich, wenn ich nach langer Zeit auf etwas zurückkomme. Erkläre dir selbst, was du denkst. Es dauert ein wenig, aber es ist relativ einfach und meistens schmerzfrei.

Ich habe manchmal zwei- oder dreimal so viele Kommentarzeilen wie ich, besonders wenn die Konzepte oder Techniken für mich neu sind und es ertragen, sich selbst zu erklären.

Führen Sie eine Versionskontrolle durch, verbessern Sie Ihre Praktiken usw. ... alles oben Genannte. Aber erkläre dir die Dinge, während du gehst. Es funktioniert wirklich gut


4

Welche Eigenschaften sind für diese Art von Programm wichtig?

Es spielt wahrscheinlich keine Rolle, ob es einfach zu warten oder weiterzuentwickeln ist, denn die Chancen stehen gut, dass dies nicht passieren wird.

Es ist wahrscheinlich egal, wie effizient es ist.

Es spielt wahrscheinlich keine Rolle, ob es eine großartige Benutzeroberfläche hat oder ob es gegen böswillige Angreifer sicher ist.

Es kann wichtig sein, dass es lesbar ist: Jemand, der Ihren Code liest, kann sich leicht davon überzeugen, dass er das tut, was er behauptet zu tun.

Es ist sicher wichtig, dass es richtig ist. Wenn das Programm falsche Ergebnisse liefert, sind das Ihre wissenschaftlichen Schlussfolgerungen aus dem Fenster. Es muss jedoch nur die Eingabe, die Sie tatsächlich verarbeiten möchten, korrekt verarbeiten. Es ist wirklich egal, ob es bei negativen Eingabedatenwerten umkippt, wenn alle Ihre Datenwerte positiv sind.

Es ist auch wichtig, dass Sie ein gewisses Maß an Änderungskontrolle beibehalten. Ihre wissenschaftlichen Ergebnisse müssen reproduzierbar sein. Das bedeutet, dass Sie wissen müssen, in welcher Version des Programms die Ergebnisse veröffentlicht wurden, die Sie veröffentlichen möchten. Da es nur einen Entwickler gibt, muss die Änderungskontrolle nicht sehr aufwendig sein, aber Sie müssen sicherstellen, dass Sie zu einem bestimmten Zeitpunkt zurückkehren und Ihre Ergebnisse reproduzieren können.

Machen Sie sich also keine Gedanken über Programmierparadigmen, Objektorientierung und algorithmische Eleganz. Sorgen Sie sich um Klarheit und Lesbarkeit sowie um die Nachvollziehbarkeit Ihrer Änderungen im Zeitverlauf. Mach dir keine Sorgen über die Benutzeroberfläche. Kümmern Sie sich nicht darum, jede mögliche Kombination von Eingabeparametern zu testen, sondern testen Sie genug, um sicher zu sein (und andere zu überzeugen), dass Ihre Ergebnisse und Schlussfolgerungen gültig sind.


4

Ich habe in einer ähnlichen Umgebung mit Akademikern gearbeitet, die viel (Mathematik- / Wissenschafts-) Code schreiben, aber ihre Fortschritte sind aus den gleichen Gründen, die Sie beschreiben, langsam. Eines ist mir jedoch aufgefallen, und ich denke, es kann Ihnen auch helfen: Eine Sammlung spezialisierter Bibliotheken aufzubauen und zu pflegen, die projektübergreifend verwendet werden können. Diese Bibliotheken sollten Dienstprogrammfunktionen bereitstellen und helfen daher, Ihr aktuelles Projekt spezifisch für die Problemdomäne zu halten.

Beispielsweise müssen Sie möglicherweise viele Koordinatentransformationen in Ihrem Bereich (ECEF, NED, Lat / Lon, WGS84 usw.) durchführen, sodass eine Funktion wie convert_ecef_to_ned()in ein neues Projekt mit dem Namen aufgenommen werden sollte CoordinateTransformations. Stellen Sie das Projekt unter Versionskontrolle und hosten Sie es auf den Servern Ihrer Abteilung, damit andere Benutzer es verwenden (und hoffentlich verbessern) können. Dann sollten Sie nach ein paar Jahren eine robuste Sammlung von Bibliotheken mit Ihren Projekten haben, die nur Code enthalten, der für ein bestimmtes Problem / einen bestimmten Forschungsbereich spezifisch ist.

Einige allgemeinere Ratschläge:

  • Versuchen Sie immer, Ihr spezielles Problem so genau wie möglich zu modellieren, egal was es ist. Auf diese Weise sollten Fragen zum Softwaredesign, wie z. B. was / wo / wie eine Variable platziert werden soll, offensichtlicher zu beantworten sein.
  • Ich würde mich nicht um testgetriebene Entwicklung kümmern, da wissenschaftlicher Code Ideen und Konzepte beschreibt und kreativer und flüssiger ist. Es sind keine APIs zu definieren, Dienste zu warten, Risiken für den Code anderer beim Ändern der Funktionalität usw.

Lass es nicht zu, dass andere es verbessern. Möglicherweise verstehen sie den Zweck des Codes nicht und bringen die Dinge nur durcheinander.
Kathreadler

@mathreadler Nun, wenn es sich um allgemeine Hilfsbibliotheken handelt, dann wird es für andere ein bisschen schwierig sein, Fehler zu machen, das ist die Idee.
Jigglypuff

Warum ist es schwierig, Universalbibliotheken durcheinander zu bringen? Es ist nicht so schwer, wenn Sie keine Ahnung haben, was Sie tun, oder wenn Sie sich wirklich anstrengen, auch nicht.
Kathreadler

@mathreadler Weil es im Allgemeinen nur eine Möglichkeit gibt, zum Beispiel Koordinatentransformationen oder Einheitenumrechnungen durchzuführen.
Jigglypuff

Es gibt normalerweise viele Möglichkeiten, je nachdem, wie Ihre Nummern im Speicher gespeichert sind, welche Darstellung sie verwenden und viele andere Dinge, für welche CPU Sie die Bibliothek kompilieren möchten. Ein Kodierer kann annehmen, dass jeder zum Beispiel immer IEEE-Doppel verwendet, ein anderer verwendet fast immer eine einfache Genauigkeit oder ein drittes seltsameres Format. Ein Codierer verwendet dann Template-Polymorphismus, aber ein anderer könnte allergisch dagegen sein, ein dritter, noch seltsamer, codiert alles in Low-Level-C oder Assembler.
Kathreadler

3

Das Folgende sind meine Meinungen und sehr stark von meinem eigenen Weg beeinflusst.

Das Codieren bringt oft dogmatische Perspektiven mit sich, wie Sie Dinge tun sollten. Anstelle von Techniken und Werkzeugen sollten Sie sich meines Erachtens die kumulierten Werte und Kosten ansehen, um eine geeignete Strategie zu finden.

Das Schreiben von gutem, lesbarem, debuggbarem, soliden Code erfordert viel Zeit und Mühe. In vielen Fällen lohnt es sich bei einem begrenzten Planungshorizont nicht, dies zu tun (Analyseparalyse).

Ein Kollege hatte eine Faustregel; Wenn Sie zum dritten Mal im Wesentlichen das Gleiche tun, investieren Sie Aufwand, andernfalls ist ein schneller und schmutziger Job angebracht.

Tests jeglicher Art sind unerlässlich, aber für einmalige Projekte kann das bloße Hinsehen ausreichend sein. Für alles Wesentliche sind Tests und Testinfrastrukturen unerlässlich. Der Wert ist, dass Sie beim Codieren davon befreit werden. Die Kosten sind, dass, wenn sich der Test auf eine bestimmte Implementierung konzentriert, auch die Tests gewartet werden müssen. Tests erinnern Sie auch daran, wie die Dinge funktionieren sollen.

Für meine eigenen einmaligen Skripte (oft für die Validierung einer Wahrscheinlichkeitsschätzung oder ähnliches) fand ich zwei kleine Dinge sehr nützlich: 1. Fügen Sie einen Kommentar ein, der zeigt, wie der Code verwendet wird. 2. Beschreiben Sie kurz, warum Sie den Code geschrieben haben. Diese Dinge sind schrecklich offensichtlich, wenn Sie den Code schreiben, aber die Zeit verschwendet die Offensichtlichkeit :-).

Bei OOP geht es darum, Code wiederzuverwenden, zu abstrahieren, zu kapseln, zu faktorisieren usw. Sehr nützlich, aber leicht zu verlieren, wenn die Erstellung von Qualitätscode und Design nicht Ihr Endziel ist. Es braucht Zeit und Mühe, um qualitativ hochwertige Produkte herzustellen.


3

Während ich denke, dass Unit-Tests ihre Vorzüge haben, sind sie für die wissenschaftliche Entwicklung von zweifelhaftem Wert - sie sind oft einfach zu klein, um viel Wert zu bieten.

Aber ich mag Integrationstests für wissenschaftlichen Code wirklich:

Isolieren Sie einen kleinen Teil Ihres Codes, der von sich aus funktionieren könnte, z. B. die ETL-Pipeline. Schreiben Sie dann einen Test, der die Daten liefert, führen Sie die etl-Pipeline (oder nur einen Schritt) aus und prüfen Sie dann, ob das Ergebnis Ihren Erwartungen entspricht. Während der getestete Block eine Menge Code sein kann, liefert der Test immer noch Wert:

  1. Sie haben einen praktischen Hook, mit dem Sie Ihren Code erneut ausführen können. Dadurch können Sie ihn häufig ausführen.
  2. Sie können einige Annahmen in Ihrem Test testen
  3. Wenn etwas kaputt geht, ist es einfach, einen fehlgeschlagenen Test hinzuzufügen und das Problem zu beheben
  4. Sie kodifizieren die erwarteten Ein- / Ausgaben und vermeiden die üblichen Kopfschmerzen, die beim Versuch entstehen, das Eingabedatenformat zu erraten.
  5. IT-Tests sind zwar nicht so schlank wie Komponententests, helfen jedoch dabei, Ihren Code aufzubrechen und Sie zu zwingen, einige Grenzen in Ihren Code einzufügen.

Ich verwende diese Technik oft und habe oft eine relativ gut lesbare Hauptfunktion, aber Unterfunktionen sind oft recht lang und hässlich, können aber aufgrund robuster E / A-Grenzen schnell geändert und neu angeordnet werden.


2

Normalerweise arbeite ich auf einer sehr großen Quellbasis. Wir verwenden alle Werkzeuge, die Sie erwähnen. Vor kurzem habe ich begonnen, an einigen Python-Skripten für ein Nebenprojekt zu arbeiten. Es sind höchstens ein paar Dutzend bis ein paar Hundert Zeilen. Aus Gewohnheit habe ich meine Skripte der Quellcodeverwaltung übergeben. Dies war nützlich, da ich Zweige zum Ausprobieren von Experimenten erstellen kann, die möglicherweise nicht funktionieren. Ich kann angeben, ob ich den Code duplizieren und für einen anderen Zweck ändern muss. Dadurch bleibt das Original erhalten, falls ich es erneut herausbringen muss.

Für "Komponententests" habe ich nur einige Eingabedateien, die eine bekannte Ausgabe erzeugen sollen, die ich von Hand überprüfe. Ich könnte es wahrscheinlich automatisieren, aber es scheint, als würde es mehr Zeit in Anspruch nehmen, als ich dadurch sparen würde. Es hängt wahrscheinlich davon ab, wie oft ich die Skripte ändern und ausführen muss. So oder so, wenn es funktioniert, machen Sie es. Wenn es mehr Mühe ist als es wert ist, verschwende nicht deine Zeit.


2

Beim Schreiben von Code - wie beim Schreiben im Allgemeinen - lautet die Hauptfrage:

Welchen Leser haben Sie im Sinn? oder Wer verbraucht Ihren Code?

Dinge wie formale Kodierungsrichtlinien machen keinen Sinn, wenn Sie Ihr einziges Publikum sind.

Andererseits wäre es hilfreich, den Code so zu schreiben, dass Sie ihn in Ihrer Zukunft sofort verstehen können.

Ein "guter Stil" wäre also einer, der Ihnen am meisten hilft. Wie dieser Stil aussehen sollte, ist eine Antwort, die ich nicht geben kann.

Ich denke, Sie brauchen keine OOP- oder Unit-Tests für Dateien von 150 LOC. Ein dediziertes VCS wäre interessant, wenn Sie sich entwickelnden Code haben. Ansonsten .bakmacht a den Trick. Diese Werkzeuge sind ein Heilmittel für eine Krankheit, die Sie vielleicht gar nicht haben.

Vielleicht sollten Sie Ihren Code so schreiben, dass Sie ihn auch dann lesen, verstehen und ändern können, wenn Sie ihn betrunken lesen.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.