web-dev-qa-db-ja.com

科学的なコードを書くときにきれいにプログラミングする

大規模なプロジェクトはあまり書いていません。私は巨大なデータベースを維持したり、数百万行のコードを処理したりしていません。

私のコードは主に「スクリプト」タイプのものです-数学関数をテストするためのもの、または何かをシミュレートするためのもの-「科学的プログラミング」。これまでに取り組んだ最も長いプログラムは数百行のコードであり、私が取り組んでいるプログラムのほとんどは約150です。

私のコードもがらくたです。先日書いたファイルを見つけようとしていたときに気づきましたが、おそらく上書きして、バージョンコントロールを使用していないため、多くの人が私の愚かさで苦しんでいます。

私のコードのスタイルは複雑で、何かを行う別の方法を指摘する古いコメントや、コピーされたコード行で埋められています。変数名は常に非常にわかりやすい説明で始まりますが、たとえば、誰かがテストしたい新しいものを追加または変更すると、コードが上にオーバーレイされて上書きされます。私は安っぽい変数名を使い始めて、ファイルがポットに入るフレームワークを持っています。

私が現在取り組んでいるプロジェクトでは、このすべてが私に大きなかみ傷を負わせるために戻ってくる段階にいます。しかし、問題は(バージョン管理を使用し、新しい反復ごとに新しいファイルを作成し、そのすべてをどこかにテキストファイルに記録することですが、状況を劇的に改善するでしょう)改善を進める方法がわかりません私の実際のコーディングスタイル。

小さなコードを書くために単体テストは必要ですか? OOPはどうですか?大規模なプロジェクトで作業するのではなく、「科学的なプログラミング」を行うときに、優れたクリーンなコードをすばやく作成するには、どのようなアプローチが適していますか?

多くの場合、プログラミング自体はそれほど複雑ではないため、これらの質問をします。私がプログラミングでテストまたは研究しているのは、数学または科学についてです。たとえば、2つの変数と1つの関数がクラスを処理する可能性がある場合、クラスは必要ですか? (これらは一般的に、プログラムの速度がより高速である方が好ましい状況であることを考慮してください-シミュレーションの25,000,000以上のタイムステップを実行しているときは、それを望んでいます。)

多分これは広すぎるかもしれません。もしそうなら、私は謝罪しますが、プログラミングの本を見ると、それらはしばしばより大きなプロジェクトで扱われているようです。私のコードはOOPを必要としませんOOP、そしてそれはすでにかなり短いので、「ああ」のようではありませんが、ファイルは1000行削減されますやります!」 「最初からやり直す」方法と、これらの小さくて高速なプロジェクトでき​​れいにプログラムする方法を知りたいです。

もっと具体的な詳細を教えていただければ幸いですが、一般的なアドバイスが多ければ多いほど役立つと思います。私はPython 3.でプログラミングしています。


誰かが重複を提案しました。標準プログラミング標準を完全に無視することについて話しているのではないことを明確にしましょう。明らかに、これらの標準が存在する理由があります。しかし、その一方で、OOPのようなコードを書くことは本当に意味がありますか?プログラムの短さによる読みやすさのレベル?

例外があります。さらに、科学的なプログラミングには、単なる標準を超えた標準が存在する可能性があります。私もそれらについて尋ねています。これは、科学的コードを書くときに通常のコーディング標準を無視する必要があるかどうかではなく、クリーンな科学的コードを書くことです!


更新

私は「1週間後ではない」種類のアップデートを追加すると思いました。あなたのアドバイスはすべて非常に役に立ちました。私は現在バージョン管理を使用しています-git、グラフィカルインターフェイス用のgit kraken。これは非常に使いやすく、ファイルを大幅にクリーンアップしました。古いファイルが残ったり、古いバージョンのコードが「念のため」コメントアウトしたりする必要がなくなりました。

また、pylintをインストールして、すべてのコードで実行しました。最初に1つのファイルが負のスコアを取得しました。それがどのようにして可能になったのかもよくわかりません。私のメインファイルはスコアが〜1.83/10で始まり、現在は9.1/10です。すべてのコードは現在、標準にかなりよく適合しています。私はまた、自分の目で行ってしまった変数名を更新して、うーん...うんざりしていて、リファクタリングするセクションを探していました。

特に、私はこのサイトで私の主な機能の1つをリファクタリングすることについて最近の質問をしました、そしてそれは今とてもきれいでずっと短いです:長く膨らんだ、if/elseで満たされた関数の代わりに、それは今半分未満ですサイズと何が起こっているかを理解するのがはるかに簡単です。

次のステップは、ある種の「単体テスト」を実装することです。つまり、アサートステートメントとtry/exceptsを使用してその中のすべての関数を調べるメインファイルで実行できるファイルを意味します。これは、おそらく最良の方法ではなく、多くの重複コードが発生します。しかし、私は読み続けて、それをよりよくする方法を理解しようとします。

また、すでに作成したドキュメントを大幅に更新し、Excelスプレッドシート、ドキュメント、関連する論文などの補足ファイルをgithubリポジトリに追加しました。ちょっと本物のプログラミングプロジェクトのようです。

だから...これはすべて言うことだと思います:ありがとう

169
heather

これは科学者にとってかなり一般的な問題です。私はそれをよく見ました、そしてそれはいつもプログラミングがあなたの仕事をするためのツールとしてあなたが側で選ぶ何かであるという事実によって生じます。

したがって、スクリプトはごちゃごちゃです。私は常識に逆らって、一人でプログラミングしていると仮定すると、これはそれほど悪くはありません!二度と書くことのほとんどに触れることはないので、 "値"(スクリプトの結果)を生成するのではなく、きれいなコードを書くのに時間がかかりすぎても、あまり効果はありません。

しかし、あなたがやったことに戻って、何かがうまくいっていることを正確に見る必要がある時が来るでしょう。さらに、他の科学者があなたのコードをレビューする必要がある場合、誰もがそれを理解できるように、コードをできるだけ明確かつ簡潔にすることが非常に重要です。

あなたの主な問題は読みやすさになるため、改善するためのヒントをいくつか紹介します。

変数名:

科学者は簡潔な表記法を使うのが大好きです。すべての数式は通常、変数として単一の文字を使用します。コードに非常に短い変数がたくさんあるのに驚かないでしょう。これは読みやすさを大きく損ないます。コードに戻ると、y、i、x2が何を表しているのか覚えていないため、多くの時間を費やしてそれを理解しようとします。代わりに、変数を正確に表す名前を使用して、変数に明示的に名前を付けてみてください。

コードを関数に分割します。

すべての変数の名前を変更したので、方程式はひどく見え、複数行になります。

メインプログラムに残すのではなく、その方程式を別の関数に移動し、それに応じた名前を付けます。これで、巨大でめちゃくちゃなコード行を使用する代わりに、何が起こっているのか、どの方程式を使用したのかを正確に伝える短い説明が表示されます。これにより、実際の方程式を調べて何をしたかを知る必要がないため、メインプログラムと方程式コード自体の両方が改善されます。別の関数では、変数に好きな名前を付けて、戻ることができます。より身近な一文字。

この考えで、特に何かがコードで複数回実行する必要があるものである場合は、何かを表すコードのすべての部分を見つけて、それらを関数に分割してみてください。コードがすぐに読みやすくなり、コードを追加しなくても同じ関数を使用できることがわかります。

簡単に言えば、これらの関数がより多くのプログラムで必要な場合は、それらのライブラリを作成するだけで、いつでも利用できます。

グローバル変数:

初心者の頃、これは自分のプログラムの多くのポイントで必要なデータを渡すのに最適な方法だと思いました。他のものを渡す方法は他にもたくさんあることがわかりました。プログラムのランダムな点に行くと、その値が最後にいつ使用または編集されたかわからないため、グローバル変数が行う唯一のことは頭痛の種です。それを追跡するのは面倒です。可能な限りそれらを避けるようにしてください。

関数が複数の値を返すか変更する必要がある場合は、それらの値を使用してクラスを作成し、それらをパラメーターとして渡すか、関数が複数の値を(名前付きタプルを使用して)返し、呼び出し元のコードでそれらの値を割り当てます。

バージョン管理

これは読みやすさを直接改善するわけではありませんが、上記のすべてを行うのに役立ちます。変更を加えるたびに、バージョン管理にコミットし(ローカルGitリポジトリで十分です)、何かが機能しない場合は、変更内容を確認するか、ロールバックしてください!これにより、コードのリファクタリングが簡単になり、誤ってデータを壊した場合の安全策になります。

これらすべてを念頭に置くと、明確でより効果的なコードを記述できるようになるだけでなく、巨大な関数や乱雑な変数をたどる必要がないので、起こり得る間違いをより早く見つけるのに役立ちます。

164
BgrWorker

ここの物理学者。行ったことがある。

あなたの問題はtoolsの選択やプログラミングパラダイム(単体テスト、OOPなど)に関するものではないと私は主張します。 態度、考え方についてです。最初に変数名が適切に選択され、最終的にがらくたになるという事実は十分に明らかにしています。コードを「一度実行してから破棄する」と考えると、必然的に混乱を招きます。クラフトと愛の産物だと思えば、キレイになるでしょう。

クリーンなコードを書くためのレシピは1つしかないと思います。それを実行するインタプリタではなく、それを読む人間のために書いてください。コードが混乱しているかどうかはインタプリタは気にしませんが、人間の読者は気にします

あなたは科学者です。おそらく科学記事を磨くのに多くの時間を費やすことができます。最初のドラフトが複雑に見える場合は、ロジックが最も自然な方法で流れるまでリファクタリングします。あなたは同僚にそれを読んで、議論が非常に明確であることを望んでいます。あなたは生徒たちがそこから学ぶことができるようにしたいのです。

クリーンなコードの記述はまったく同じです。あなたのコードは、たまたま機械可読であるアルゴリズムの詳細な説明と考えてください。あなたがそれを人々が読む記事として公開することを想像してみてください。あなたはそれを会議で示し、聴衆に行ごとに説明します。さて、 プレゼンテーションのリハーサル 。はい、行ごと!恥ずかしいですね。スライドをクリーンアップして(エラー...つまり、コード)、もう一度リハーサルを行います。結果に満足するまで繰り返します。

リハーサルの後、想像上の人々とあなたの将来の自分だけでなく、実際の人々にあなたのコードを示すことができればなお良いでしょう。行ごとにそれを実行することは「コードウォーク」と呼ばれ、ばかげた習慣ではありません。

もちろん、これにはすべてコストがかかります。クリーンなコードを書くには、使い捨てコードを書くよりもたくさん時間がかかります。特定のユースケースのコストをメリットが上回るかどうかを評価できるのは、あなただけです。

ツールについては、前に述べましたが、それは重要ではありません。ただし、1つを選択する必要がある場合は、バージョン管理が最も便利です。

141
Edgar Bonet

バージョン管理は多分あなたにあなたのお金のために最も大きな力を与えるでしょう。これは長期保存だけでなく、短期間の実験を追跡し、最後に機能したバージョンに戻ってメモを付けておくのに最適です。

次に最も役立つのは、単体テストです。ユニットテストについて重要なのは、数百万行のコードを含むコードベースでさえ、一度に1つの関数でユニットテストされることです。ユニットテストは、抽象化の最低レベルで小規模に行われます。つまり、小さなコードベース用に記述されたユニットテストと、大きなコードベース用に記述されたユニットテストとの間に基本的に違いはありません。それらはさらに多くあります。

単体テストは、他の何かを修正するときにすでに機能していたものを壊さないようにするため、または少なくとも修正したときにすばやく通知するための最良の方法です。それらは実際にはmoreプログラマーのスキルが低い場合、またはエラーの可能性を低くしたり、明白にしたりするように構成された詳細なコードを記述したくない、またはしたくない場合に役立ちます。

バージョン管理とユニットテストの記述の間で、コードは当然よりクリーンになります。プラトーに達したときに、よりクリーンなコーディングのための他のテクニックを学ぶことができます。

81
Karl Bielefeldt

(バージョン管理を使用し、新しいイテレーションごとに新しいファイルを作成し、そのすべてをどこかにテキストファイルに記録することを除いて、状況を劇的に改善します)

あなたはおそらくこれを自分で理解したでしょうが、「どこかすべてをテキストファイルに記録する必要がある場合」、バージョンを使用していません制御システムを最大限に活用します。 Subversion、git、またはMercurialなどを使用し、コミットごとに適切なコミットメッセージを書き込むと、テキストファイルの目的にかなうログが得られますが、リポジトリから切り離すことはできません。

それはさておき、既存の回答で言及されていない理由により、バージョン管理を使用することが最も重要なことです:結果の再現性。ログメッセージを使用するか、結果にリビジョン番号を付けてメモを追加できる場合は、結果を再生成できるはずであり、紙でコードを公開するほうが適切です。

小さなコードを書くために単体テストは必要ですか? OOPはどうですか?大規模なプロジェクトで作業するのではなく、「科学的なプログラミング」を行うときに、優れたクリーンなコードをすばやく作成するには、どのようなアプローチが適していますか?

ユニットテストは必ずしも必要ではありませんが、次の場合に役立ちます。(a)コードがモジュール全体であり、全体ではなくユニットをテストできる場合。 (b)テストを作成できます。理想的には、コードで生成するのではなく、予想される出力を手動で書き込むことができますが、コードで生成すると、少なくとも何かが変更されたかどうかを通知する回帰テストが提供されますその動作。テストがテストしているコードよりもバグがある可能性が高いかどうかを検討してください。

OOPはツールです。役立つ場合に使用しますが、それが唯一のパラダイムではありません。私はあなたが本当に手続き型プログラミングだけを知っていると思います:その場合は、説明されているコンテキストでは、OOPよりも関数型プログラミングを学ぶことの方が有益だと思います。 Pythonは、非常に機能的なスタイルで記述できます。

29
Peter Taylor

大学院では、アルゴリズムを多用したコードを自分で作成しました。クラックするのは少し難しいです。大雑把に言うと、多くのプログラミング規約は、情報をデータベースに入れ、適切なタイミングで取得し、そのデータをマッサージしてユーザーに提示するという考え方に基づいて構築されています。そのプロセスのアルゴリズムが多い部分。これらのプログラムの場合、OOPについて聞いたすべてのこと、コードを短い関数に分解し、可能な場合は一目ですべてを一目で簡単に理解できるようにすることは優れたアドバイスです。しかし、これは、アルゴリズムを多用したコードや、複雑な数学的計算を実装するコードだけではまったく機能しません。

科学計算を実行するためのスクリプトを作成している場合は、使用する方程式またはアルゴリズムが記述された論文がおそらくあります。自分で見つけた新しいアイデアを使用している場合は、自分の論文でそれらを公開することを期待しています。この場合のルールは次のとおりです。公開された方程式にできるだけ近い形でコードを読み取らせます。これがSoftware Engineering.SEの回答です。このアプローチを支持し、それがどのように見えるかを説明する200以上の賛成投票で 短い変数名の言い訳はありますか?

別の例として、物理の研究とエンジニアリングに使用される物理シミュレーションツールである Simbody には、いくつかの優れたコードスニペットがあります。これらのスニペットには、計算に使用されている方程式を示すコメントがあり、その後に、実装されている方程式にできるだけ近いコードが続きます。

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);
21
Kevin

ですから、私の一日の仕事は、カリフォルニア大学システムの研究データの公開と保存です。何人かの人々が再現性について言及しました、そして私はそれがここで本当に中心的な問題だと私は思います:誰かが実験を再現するために必要な他の何かを文書化する方法でコードを文書化すること、そして理想的には、他の誰かのためにそれを簡単にするコードを書くこと実験を再現し、エラーの原因について結果を確認します。

しかし、私が重要だと思う、私が言及していないことの1つは、資金提供機関がデータ公開の一部としてソフトウェア公開をますます重視し、ソフトウェア公開をオープンサイエンスの要件にすることです。

そのために、一般的なソフトウェア開発者ではなく研究者を対象とした特定のものが必要な場合、 Software Carpentry 組織を十分に推奨することはできません。あなたが彼らのいずれかに出席することができるなら workshops 、素晴らしい;時間とアクセスのすべてが 科学計算のベストプラクティス に関するいくつかの論文を読むことである場合も、それは良いことです。後者から:

科学者は通常、これらの目的のために独自のソフトウェアを開発します。これを行うには、ドメイン固有の知識がかなり必要になるためです。その結果、最近の研究では、科学者は通常、ソフトウェアの開発に30%以上の時間を費やしていることがわかりました。ただし、それらの90%以上は主に独学で学んでいるため、保守可能なコードの記述、バージョン管理と課題追跡の使用、コードレビュー、ユニットテスト、タスクの自動化などの基本的なソフトウェア開発手法に触れていません。

ソフトウェアは単なる別の種類の実験装置であり、物理的な装置と同じくらい注意深く作成、チェック、使用する必要があると私たちは信じています。ただし、ほとんどの科学者は実験室やフィールド機器の検証に注意を払っていますが、ほとんどの科学者はソフトウェアの信頼性を知りません。これは、公表された研究の中心的な結論に影響を与える深刻なエラーにつながる可能性があります。 …

さらに、ソフトウェアは複数のプロジェクトで使用されることが多く、他の科学者によって再利用されることが多いため、計算エラーは科学プロセスに不釣り合いな影響を与える可能性があります。このタイプの連鎖的な影響により、別のグループのコードからのエラーが公開後まで発見されなかった場合、いくつかの顕著な撤回が発生しました。

彼らが推奨する実践の概要:

  1. コンピュータではなく人のためのプログラムを書く
  2. コンピュータに仕事をさせましょう
  3. 増分変更を行う
  4. 自分(または他の人)を繰り返さないでください
  5. 間違いを計画する
  6. 正しく機能した後でのみソフトウェアを最適化する
  7. 力学ではなく文書の設計と目的
  8. 協力する

このペーパーでは、これらの各ポイントについてかなり詳しく説明しています。

17
David Moles

OOPというコードを書くことは本当に理にかなっていますか?標準的なものが実行できた場合、書き方がはるかに速くなり、プログラムの短さ?

個人的な答え:
私は科学的な目的でも多くのスクリプトを作成しています。小さなスクリプトの場合は、一般的なプログラミングの慣行(つまり、バージョン管理を使用し、変数名を使用して自己管理を実践する)に従っているだけです。データセットをすばやく開いたり、視覚化したりするために何かを書いているだけなら、OOPを気にする必要はありません。

一般的な回答:
"場合によります。"ただし、プログラミングの概念やパラダイムをいつ使用するかを理解するのに苦労している場合は、以下の点を考慮してください。

  • スケーラビリティ:スクリプトはスタンドアロンになるのでしょうか、それとも最終的にはより大きなプログラムで使用されるのでしょうか?もしそうなら、OOPを使用するより大きなプログラミングですか?スクリプトのコードをより大きなプログラムに簡単に統合できますか?
  • モジュール性:一般に、コードはモジュール化する必要があります。ただし、OOPは、非常に特殊な方法でコードをチャンクに分割します。そのタイプのモジュール性(つまり、スクリプトをクラスに分割)は、実行していることに意味がありますか?

「最初からやり直す」方法と、これらの小さくて高速なプロジェクトでき​​れいにプログラムする方法を知りたいです。

#1:そこにあるものに慣れる:
スクリプトを作成しているだけでも(そして実際に科学コンポーネントを気にしているだけでも)、さまざまなプログラミングの概念とパラダイムについて学ぶには、少し時間がかかるはずです。そうすることで、何を使用したい/使用したくないか、いつ使用したいかについて、より良いアイデアを持つことができます。それは少し気が遠くなるかもしれません。そして、「どこから始めようか、何から始めればよいか」という疑問が残るかもしれません。次の2つの箇条書きで、良い出発点を説明しようとします。

#2:あなたが間違っていることがわかっているものを修正し始めます:
個人的には、私が間違っているとわかっていることから始めます。いくつかのバージョン管理を取得し、それらの変数名をより良くするために自分自身を訓練し始めてください(それは深刻な闘争です)。あなたが間違っているとわかっているものを修正することは明白に聞こえるかもしれません。しかし、私の経験では、1つのものを修正すると他の何かにつながることがわかりました。気付かないうちに、私が間違っていた10の異なることを明らかにし、それらを修正する方法や、クリーンな方法でそれらを実装する方法を理解しました。

#3:プログラミングパートナーを取得:
「最初からやり直す」ことで正式なクラスを受講しない場合は、開発者とチームを組んで、コードのレビューを依頼することを検討してください。彼らがあなたがしていることの科学的な部分を理解していなくても、彼らはあなたのコードをよりエレガントにするためにあなたが何ができたかをあなたに伝えることができるかもしれません。

#4:コンソーシアムを探す:
あなたがどの科学分野にいるかはわかりません。しかし、科学の世界で何をしているのかに応じて、コンソーシアム、ワーキンググループ、または会議の参加者を探してみてください。次に、彼らが取り組んでいる標準があるかどうかを確認します。それはあなたをいくつかのコーディング標準に導くかもしれません。例えば、私は多くの地理空間の仕事をしています。会議の論文やワーキンググループを見て、私は Open Geospatial Consortium に進みました。彼らが行うことの1つは、地理空間開発の標準に取り組むことです。

お役に立てば幸いです。


16
JustBlossom

Unixの原則を守ることをお勧めします:Keep It Simple、Stupid! (キス)

または、別の言い方をすると:一度に1つのことを実行し、うまく実行します

どういう意味ですか?さて、まず第一に、それはあなたの機能が短くあるべきであることを意味します。目的、使用法、実装が数秒で完全に理解できない機能は、明らかに長すぎます。一度に複数の処理を実行している可能性があり、それぞれが独自の機能である必要があります。だからそれを分割します。

コード行に関しては、私のヒューリスティックは、10行は適切な関数であり、20行を超えるものはほとんどがらくたであるということです。他の人々は他のヒューリスティックを持っています。重要な部分は、長さを実際に瞬時に把握できるものに抑えることです。

長い関数をどのように分割しますか?さて、最初にコードの繰り返しパターンを探します。次に、これらのコードパターンをfactoroutして、わかりやすい名前を付け、コードshrinkを監視します。実際、最善のリファクタリングは、コードサイズを縮小するリファクタリングです。

これは、問題の機能がコピーと貼り付けでプログラムされている場合に特に当てはまります。このような繰り返しパターンを見ると、すぐにそれが独自の機能に変わる可能性が高いことがすぐにわかります。これがDo n't Repeat Yourself(DRY)の原則です。コピーと貼り付けを実行しているときはいつでも、あなたは何か間違ったことをしています!代わりに関数を作成します。

付属書
私はかつて数ヶ月を費やして、それぞれ約500行の関数を持つコードをリファクタリングしました。完了後、コード全体は約1000行短くなりました。コードの行に関して、否定的な出力を生成しました。私は会社を借りています( http://www.geekherocomic.com/2008/10/09/programmers-salary-policy/index.html )。それでも、これは今までで最も価値のある作品の1つだと固く信じています...

一部の関数は、いくつかの異なることを次々に実行しているため、長くなる場合があります。これらはDRY違反ではありませんが、分割することもできます。その結果、元の関数の個々のステップを実装する一連の関数を呼び出す高レベルの関数が頻繁に発生します。これにより、通常、コードサイズですが、追加された関数名はコードを読みやすくするために不思議に機能します。これで、すべてのステップが明示的に指定されたトップレベルの関数が作成されたためです。また、この分割後、どのステップがどのデータを操作するかがわかります。(関数引数。グローバル変数を使用しませんか?)

この種のセクション関数分割の優れたヒューリスティックは、セクションコメントを書きたくなったとき、またはコードでセクションコメントを見つけたときです。これは、おそらく関数を分割する必要があるポイントの1つです。セクションコメントは、新しい関数の名前を付けるのにも役立ちます。

KISSおよびDRYの原則は、長い道のりを歩むことができます。OOPなどで始める必要はありません。多くの場合、これら2つを適用するだけで大​​幅な簡素化を実現できることがよくあります。ただし、OOPと他のパラダイムについて知っておくと、長い目で見ればメリットがあります。プログラムコードをより明確にするため。

最後に、コミットですべてのアクションを記録します。新しい関数に何かを因数分解すると、それがコミットになります。あなたは2つの関数を1つに融合します、なぜならそれらは本当に同じことをするからですそれはコミットです。変数の名前を変更すると、それがコミットになります。 頻繁にコミットします。1日経ってもコミットしなかった場合は、何か間違ったことをした可能性があります。

私は、バージョン管理があなたの問題の多くをすぐに解決することで他の人に同意します。具体的には:

  • どのバージョンの変更が行われたか、またはファイルのコピーがたくさんあるなどのリストを維持する必要はありません。バージョン管理がそれを行うためです。
  • 上書きなどによりファイルが失われることはありません(基本に固執する限り、たとえば「履歴の書き換え」は避けてください)。
  • 時代遅れのコメントやデッドコードなどを「念のため」に置いておく必要はありません。バージョン管理に取り組んだら、遠慮なくそれらを核にしてください。これは非常に解放感があります!

考えすぎないでください。gitを使用してください。単純なコマンド(たとえば、単一のmasterブランチのみ)に固執し、おそらくGUIを使用すれば、大丈夫です。おまけとして、無料の公開とバックアップのためにgitlab、githubなどを使用できます;)

私がこの回答を書いた理由は、私が上記で見たことのない2つのことを試してみることです。 1つ目は、単体テストの軽量な代替手段としてassertionsを使用することです。単体テストは、テスト対象の関数/モジュール/何でも「外」にある傾向があります。通常、これらはデータを関数に送信し、結果を受け取り、その結果のいくつかのプロパティを確認します。これは一般的には良い考えですが、いくつかの理由で(特に「捨てる」コードの場合)不便かもしれません。

  • ユニットテストは、関数に与えるデータを決定する必要があります。そのデータは現実的でなければなりません(そうでなければデータをテストするポイントがほとんどありません)、正しい形式でなければなりません。
  • 単体テストは、主張したいことに「アクセス」できる必要があります。特に、ユニットテストでは、関数内の中間データをチェックできません。その機能を小さな部分に分解し、それらの部分をテストして、どこか他の場所に接続する必要があります。
  • ユニットテストもプログラムに関連していると想定されています。たとえば、テストスイートは、最後に実行されてから大きな変更があった場合に「古く」なり、使用されなくなったコードのテストが多数行われる場合もあります。

アサーションには、プログラムの通常の実行中にチェックされるため、これらの欠点はありません。特に:

  • これらは通常のプログラム実行の一部として実行されるため、実際に使用できる実際のデータがあります。これは個別のキュレーションを必要とせず、(定義により)現実的であり、正しい形式を持っています。
  • アサーションはコード内の任意の場所に記述できるため、チェックしたいデータにアクセスできる場所ならどこにでもアサーションを配置できます。関数の中間値をテストしたい場合は、その関数の真ん中にいくつかのアサーションを入れるだけです!
  • インラインで記述されているため、アサーションはコードの構造と「非同期」になることはありません。デフォルトでアサーションがチェックされていることを確認すると、次回プログラムを実行したときにアサーションがパスするかどうかがすぐにわかるため、アサーションが「古くなる」ことを心配する必要もありません。

速度を要因として言及しますが、その場合、アサーションチェックは望ましくない可能性がありますそのループ内(ただし、セットアップのチェックとその後の処理には役立ちます)。ただし、アサーションのほとんどすべての実装は、それらをオフにする方法を提供します。たとえば Pythonの場合-Oオプションを使用して実行すると、無効にできるようです(これまで、アサーションを無効にする必要性を感じたことがないので、これは知りませんでした)。 。デフォルトでonのままにしておくことをお勧めします。コーディング/デバッグ/テストのサイクルが遅くなる場合は、データのサブセットを小さくしてテストするか、テスト中に一部のシミュレーションの反復回数を少なくするなどの方が良いでしょう。パフォーマンス上の理由でテスト以外の実行でアサーションを無効にする場合、私が最初に行うことをお勧めしますmeasureそれらが実際にスローダウンの原因である場合! (パフォーマンスのボトルネックになると、自分をだますのは非常に簡単です)

最後のアドバイスは、依存関係を管理するビルドシステムを使用することです。私は個人的にこれに Nix を使用していますが、 Guix についても良いことを聞いています。 Dockerのような代替手段もあります。これらは、科学的な観点からはあまり有用ではありませんが、おそらくもう少し慣れています。

Nixのようなシステムはごく最近(少し)普及してきており、あなたが説明するような「捨てる」コードには過剰だと考える人もいるかもしれませんが、科学計算の再現性に対するそれらの利点は計り知れません。次のような実験を実行するためのシェルスクリプトを検討してください(例:run.sh):

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

代わりに、次のようにNixの「派生」に書き直すことができます(例:run.nix):

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

''...''の間は、以前と同じようにbashコードです。ただし、${...}を使用して、他の文字列(この場合は./.のコンテンツに「スプライス」することができます。 run.nix)を含むディレクトリのパスに展開されます。 with import ...行は、Nixの 標準ライブラリ をインポートします。これは、bashコードを実行するためのrunCommandを提供します。 nix-build run.nixを使用して実験を実行できます。これにより、/nix/store/1wv437qdjg6j171gjanj5fvg5kxc828p-output.csvのようなパスが提供されます。

では、これで何が買えるのでしょうか? Nixは自動的に「クリーン」な環境をセットアップします。この環境は、明示的に要求したものにのみアクセスできます。特に、$HOMEなどの変数や、インストールしたシステムソフトウェアにはアクセスできません。これにより、~/.configのコンテンツやインストールしたプログラムのバージョンなど、現在のマシンの詳細とは関係なく結果が得られます。 AKA他の人が私たちの結果を複製するのを防ぐもの!そのため、デフォルトではプロジェクトにアクセスできないため、cpコマンドを追加しました。システムのソフトウェアがNixスクリプトで利用できないのは不愉快に思えるかもしれませんが、それは逆のことも言えます。スクリプトでそれを使用するために、システム(Nix以外)に何かをインストールする必要はありません。要求するだけで、Nixは必要なものをすべてフェッチ/コンパイル/フェッチします(ほとんどのものはバイナリとしてダウンロードされます。標準ライブラリも巨大です!)。たとえば、特定のPythonおよびHaskellパッケージの束が必要な場合、それらの言語の特定のバージョンに加えて、他のジャンクも必要です(理由は何故ですか?):

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

同じnix-build run.nixがこれを実行し、最初に要求したすべてをフェッチします(後で必要になる場合に備えてすべてキャッシュします)。出力($outと呼ばれる任意のファイル/ディレクトリ)は、Nixによって保存されます。これは、出力するパスです。これは、私たちが要求したすべての入力(スクリプトの内容、他のパッケージ、名前、コンパイラフラグなど)の暗号化ハッシュによって識別されます。これらの他のパッケージはtheir入力のハッシュで識別されます。これにより、bashをコンパイルしたGCCのバージョンをコンパイルしたGCCのバージョンに戻って、すべてに対して完全な地方のチェーンができます。等々!

うまくいけば、これにより科学的なコードが大量に購入され、使い始めるのがかなり簡単であることを示しました。それはまた、科学者、例えば(Googleの上位ヒット) https://dl.acm.org/citation.cfm?id=2830172 なので、(プログラミングと同じように)育成する価値のあるスキルかもしれません

11
Warbo

本格的なバージョン管理+パッケージング+単体テストのような考え方(ある時点で達成しようとする優れたプログラミング手法)に行かずに、私が当てはまると思う中間ソリューションの1つは Jupiterノートブック 。これは科学計算とよりよく統合するようです。

これには、考えとコードを組み合わせることができるという利点があります。あるアプローチが他のアプローチよりも優れている理由を説明し、古いコードをそのままにして、その場限りのセクションで説明します。セルを適切に使用することに加えて、自然にコードを断片化し、理解を助けることができる関数にコードを編成するようになります。

上位の回答は既に良好ですが、いくつかの質問に直接回答したいと思います。

小さなコードを書くために単体テストは必要ですか?

コードのサイズは、単体テストの必要性とは直接関係ありません。それは間接的に関連しています:ユニットテストはcomplexコードベースでより価値があり、小さなコードベースは一般的に大きなものほど複雑ではありません。

ユニットテストは、間違いを犯しやすいコードや、このコードの多くの実装が必要になるコードに適しています。単体テストはcurrentの開発に役立つことはほとんどありませんが、existingコードが突然誤動作する原因となる将来のミスを防ぐために多くのことを行いますそのことに触れないでください)。

ライブラリAが数値の2乗を実行し、ライブラリBがピタゴラスの定理を適用するアプリケーションがあるとします。明らかに、BはAに依存しています。ライブラリAで何かを修正する必要があり、数値を2乗するのではなく、3乗するというバグを導入するとします。

ライブラリーBが突然動作を開始し、例外がスローされるか、単に誤った出力が出力される可能性があります。ライブラリBのバージョン履歴を見ると、変更されていないことがわかります。問題のある最終結果は、何が問題になっている可能性があるかを示しておらず、問題がAにあることに気づく前にBの動作をデバッグする必要があるということです。それは無駄な努力です。

単体テストを入力します。これらのテストは、ライブラリAが意図したとおりに機能していることを確認します。ライブラリAに悪い結果を返すバグを導入すると、単体テストでそれが検出されます。したがって、ライブラリBをデバッグしようとするのに行き詰まることはありません。
これは範囲外ですが、継続的な統合開発では、誰かがコードをコミットするたびにユニットテストが実行されます。

特に複雑な数学演算の場合、単体テストは祝福になる可能性があります。いくつかの例の計算を行ってから、計算された出力と実際の出力(同じ入力パラメーターに基づく)を比較する単体テストを記述します。

ただし、単体テストはcreate優れたコードではなく、-maintainには役立ちます。通常、コードを1回記述し、それを再訪しない場合、単体テストはあまり効果がありません。

OOPはどうですか?

OOPは、たとえば次のような個別のエンティティについて考える方法です。

CustomerProductを購入したい場合、Vendorに話しかけてOrderを受け取ります。次に、AccountantVendorを支払います。

これを、関数型プログラマーが物事をどう考えているかと比較してください。

顧客がpurchaseProduct()を希望する場合、talktoVendor()を使用するため、sendOrder()を使用できます。その後、アカウンタントはpayVendor()になります。

リンゴとオレンジ。どちらも客観的に他より優れていません。注意すべき興味深い点の1つは、OOPの場合、Vendorが2回言及されていますが、同じことを指します。ただし、関数型プログラミングの場合、talktoVendor()payVendor()は2つの別個のものです。
これはアプローチの違いを示しています。これら2つのアクションの間にベンダー固有の共有ロジックがたくさんある場合、OOPはコードの重複を減らすのに役立ちます。ただし、2つの間に共有ロジックがない場合は、それらを1つにマージしますVendorは無駄な作業です(そのため、機能プログラミングはより効率的です)。

多くの場合、数学的および科学的計算は、暗黙の共有ロジック/式に依存しない別個の操作です。そのため、関数型プログラミングはOOPよりも頻繁に使用されます。

大規模なプロジェクトで作業するのではなく、「科学的なプログラミング」を行うときに、優れたクリーンなコードをすばやく作成するには、どのようなアプローチが適していますか?

あなたの質問は、「良い、きれいなコード」の定義が、科学的プログラミングをしている場合でも、大規模な(つまり、エンタープライズを意味する)プロジェクトで作業している場合でも変わることを示唆しています。

適切なコードの定義は変わりません。 複雑さを回避する必要がある(クリーンなコードを記述することで実行できます)は変更されます。

同じ議論がここに戻ってきます。

  • 古いコードを再訪せず、コードをコンパートメント化することなくロジックを完全に理解する場合は、保守を容易にするために過度の労力を費やさないでください。
  • 古いコードを再検討する場合、または必要なロジックが複雑すぎて一度にすべてを処理できない場合(したがって、ソリューションを区分化する必要がある場合)、クリーンで再利用可能なクローズの記述に集中してください。

多くの場合、プログラミング自体はそれほど複雑ではないため、これらの質問をします。私がプログラミングでテストまたは研究しているのは、数学または科学についてです。

ここであなたがしている区別はわかりますが、既存のコードを振り返ってみるとすると、数学とプログラミングの両方を見ていることになります。 どちらかが人為的または複雑な場合、それを読むのに苦労します。

たとえば、2つの変数と1つの関数がクラスを処理する可能性がある場合、クラスは必要ですか?

OOPの原則はさておき、いくつかのデータ値を格納するためにクラスを作成する主な理由は、簡略化メソッドのパラメーターと戻り値を宣言するためです。たとえば、場所(緯度/経度のペア)を使用する多くのメソッドがある場合、float latitude, float longitudeと入力するのにすぐに飽きてしまい、Location locと書く方がずっと好きになります。

これは、メソッドが一般に1つの値を返し(言語固有の機能がより多くの値を返すために存在しない場合)、場所などの2つの値(lat + lon)を返すことを考えると、さらに複雑になります。これにより、Locationクラスを作成してコードを簡略化することができます。

たとえば、2つの変数と1つの関数がクラスを処理する可能性がある場合、クラスは必要ですか?

注目すべきもう1つの興味深い点は、OOPをデータ値とメソッドを混合せずに使用できることです。すべての開発者がここで同意するわけではありません(アンチパターンと呼ばれることもあります)が、 データクラス(値フィールドを格納)とロジッククラス(メソッドを格納)を分離します。
もちろん、これはスペクトルに関するものです。あなたは完全に貧血である必要はありません、あなたがそれが適切であると考えるとき、あなたはそれを使うことができます。

たとえば、人の姓と名を単純に連結するメソッドは、実際には「ロジック」ではなく計算された値であるため、Personクラス自体に含めることができます。

(これらは一般的に、プログラムの速度がより高速である方が好ましい状況であることを考慮してください-シミュレーションの25,000,000以上のタイムステップを実行しているときは、それを望んでいます。)

クラスは常にそのフィールドの合計と同じ大きさです。再び2つのLocation値で構成されるfloatの例を取り上げますが、ここでは、1つのLocationオブジェクトが2つの個別のfloat値と同じだけのメモリを消費することに注意してください。

その意味では、OOPを使用しているかどうかは関係ありません。メモリフットプリントは同じです。

パフォーマンス自体も、大きなハードルではありません。たとえば、グローバルメソッドまたはクラスメソッドの使用は、ランタイムパフォーマンスとは関係ありませんが、コンパイル時のバイトコードの生成とはすべて関係があります。

このように考えてください。ケーキのレシピを英語で書いてもスペイン語で書いても、ケーキを焼くのに30分かかる(=ランタイムパフォーマンス)という事実は変わりません。レシピの言語が変更されるのは、料理人が材料をどのように混合するか(=バイトコードをコンパイルすること)だけです。

For Python具体的には、コードを呼び出す前に明示的にプリコンパイルする必要はありません。ただし、プリコンパイルしない場合、コンパイルを実行すると、コードを実行します。「ランタイム」とは、実行自体のことであり、実行に先行する可能性のあるコンパイルではありません。

6
Flater

貿易のツールは通常、ニーズを解決するために発明されました。必要がある場合はツールを使用し、そうでない場合はおそらく使用する必要はありません。

具体的には、科学的プログラムは最終目標ではなく、手段です。あなたは今ある問題を解決するためのプログラムを書いています-そのプログラムが他の人たちによって10年間使われる(そして維持される必要がある)とは期待していません。それだけでは、現在の開発者がバージョン管理などの他の人の履歴を記録したり、キャプチャしたりできるようにするツールを考えたりしたりしない単体テストのようなコードの機能。

それでは何があなたに利益をもたらすでしょうか?

  • 非常に簡単に作業をバックアップできるため、バージョン管理は素晴らしいです。 2018年現在、githubは非常に人気のある場所です(必要に応じて、後でいつでも移動できます-gitは非常に柔軟です)。バックアップの安くて簡単な代用は、オペレーティングシステムの自動バックアップ手順です(M​​acの場合はTime Machine、Linuxの場合はrsyncなど)。あなたのコードは複数の場所にある必要があります!
  • ユニットテストはいいですfirstと書くと、コードが実際に何を実行するかを確認する方法を考えざるを得なくなります。コードのAPI。これは、後で再利用するコードの記述に取り掛かった場合に役立ち、アルゴリズムを変更する際に役立ちます。アルゴリズムがこれらのケースで機能することがわかっているためです。
  • ドキュメンテーション。使用するプログラミング言語(Javaなどの場合はjavadoc)で適切なドキュメントを作成する方法を学びます。将来のために作成します。このプロセスでは、適切な変数名がドキュメント化を容易にすることがわかります。繰り返します。詩人が詩を書くのと同じくらい注意をドキュメントに与えてください。
  • 優れたツールを使用してください。 IDE thathelpsを見つけて、それをよく学んでください。変数をより良い名前に変更するようなリファクタリングは、これがはるかに簡単です仕方。
  • ピアがいる場合は、ピアレビューの使用を検討してください。部外者にあなたのコードを見て理解してもらうことは、あなたが書く未来の今現在のバージョンです。相手が自分のコードを理解していない場合は、おそらく後で理解することもできません。

クリーンな科学的コードの利点

  • ...プログラミングの本を見ると、それらはしばしばより大きなプロジェクトで扱われているようです。

  • ... OOPというコードを書くことは本当に理にかなっていますか?標準的なことを行うことができ、書き方がはるかに高速で、読みやすさのレベルも同じだったでしょう。プログラムの短さのために?

将来のコーダーの観点からコードを検討すると役立つ場合があります。

  • なぜ彼らはこのファイルを開いたのですか?
  • 彼らは何を探していますか?

私の経験から、

クリーンなコードは結果を簡単に検証できるはずです

  • ユーザーがプログラムを実行するために何をする必要があるかを正確に把握しやすくします。
  • 個々のアルゴリズムを個別にベンチマークできるように、プログラムを分割することもできます。

  • 1つの無関係な操作が別の操作の動作を変える原因となる、直感に反する副作用を伴う関数の記述は避けてください。回避できない場合は、コードに必要なものとその設定方法を文書化してください。

クリーンなコードは、将来のコーダーのサンプルコードとして機能します

明確なコメント(関数の呼び出し方法を示すコメントを含む)と適切に分離された関数は、誰かが(または将来あなたが)始めたばかりの人があなたの仕事から有用なものを作るのにかかる時間に大きな違いを生む可能性があります。

これに加えて、他の誰かが使用できるようにスクリプトを実際のライブラリに作成することを決定した場合、アルゴリズムに実際の「API」を作成すると、より適切に準備できます。

推奨事項

コメントを使用して数式を「引用」します。

  • 特に最適化を使用した場合(トリガーID、テイラー級数など)、コメントを「引用」数式に追加します。
  • 本から式を入手した場合は、John Smith Method from Some Book 1st Ed. Section 1.2.3 Pg 180、ウェブサイトや論文で公式を見つけた場合は、それも引用してください。
  • 「リンクのみ」のコメントは避けることをお勧めします。方法をどこかで参照して、人々がそれをグーグルできるようにしてください。古い内部ページにリダイレクトされたいくつかの「リンクのみ」のコメント 非常にイライラする になる可能性があります。
  • それでもUnicode/ASCIIで読みやすい場合は、コメントに数式を入力してみることができますが、これは非常に扱いにくい場合があります(コードコメントはLaTeXではありません)。

コメントを賢く使う

適切な変数名/関数名を使用してコードの読みやすさを改善できる場合は、最初にそれを行ってください。コメントは削除するまで永久に残るので、古くならないコメントを作成してください。

説明的な変数名を使用する

  • 式の一部である場合は、1文字の変数が最適なオプションです。
  • 将来の読者にとって、記述したコードを見て、実装している方程式と比較できることが重要になる場合があります。
  • 適切な場合は、接尾辞を追加して、実際の意味を説明することを検討してください。 xBar_AverageVelocity
  • 前述のように、コメントのどこかに、使用している数式/メソッドを名前で明示することをお勧めします。

既知の良いデータと既知の悪いデータに対してプログラムを実行するコードを記述します。

小さなコードを書くために単体テストは必要ですか?

ユニットテストは役立つと思います。科学的コードのユニットテストの最良の形態は、既知の不良データと良好データで実行される一連のテストです。

アルゴリズムを実行するコードを記述し、結果が予想とどの程度異なるかを確認します。これは、誤って何かを誤ってハードコーディングして、誤検知の結果を引き起こしたり、関数が常に同じ値を返すような間違いを犯したりする(非常に悪い可能性があり、見つけにくい)問題を見つけるのに役立ちます。

これは、抽象化のどのレベルでも実行できることに注意してください。たとえば、パターンマッチングアルゴリズム全体をテストしたり、最適化プロセスの2つの結果の間の距離を計算するだけの関数をテストしたりできます。最初に結果にとって最も重要な領域、および/または最も懸念しているコードの部分から始めます。

新しいテストケースの追加を容易にし、「ヘルパー」関数の追加を検討し、入力データを効果的に構造化します。これは、テストを簡単に再実行できるように、入力データをファイルに保存する可能性があることを意味しますが、誤検知や偏った、または自明に解決されたテストケースを回避するように非常に注意してください。

相互検証 のようなものの使用を検討してください。詳細は 相互検証に関するこの投稿 を参照してください。

バージョン管理を使用する

バージョン管理を使用して、外部サイトでリポジトリをホストすることをお勧めします。無料でリポジトリをホストするサイトがあります。

利点:

  1. ハードディスクが故障した場合のバックアップを提供します
  2. 履歴が提供されるため、最近発生した問題が誤ってファイルを変更したことが原因で発生したかどうかを心配する必要がなくなります。
  3. ブランチを使用することができます。これは、無関係な作業に影響を与えずに長期的/実験的なコードで作業するための良い方法です。

コードをコピーして貼り付けるときは注意してください

私のコードのスタイルは複雑で、何かを行う別の方法を指摘する古いコメントや、コピーされたコード行で埋められています。

  • コードをコピーして貼り付けることで時間を節約できますが、特に自分で記述していないコード(たとえば、同僚からのコード)の場合は、実行できる最も危険なことの1つです。

  • コードが機能してテストされたらすぐに、変数の名前を変更したり、理解できないものにコメントを付けたりするために、コードを慎重に検討することをお勧めします。

6
jrh

バージョン管理と単体テストは、コード全体を整理して機能させるためのものであるだけでなく、よりクリーンなコードを作成するのにも役立ちません。

  • バージョン管理により、コードがいつ、どのように乱雑になったかを確認できます。
  • ユニットテストは、コードが完全に混乱しているにもかかわらず、まだ機能することを確認します。

乱雑なコードを書くのをやめたい場合は、乱雑が発生する場所で機能するツールが必要です。コードを書き込んでいるときです。よく使われる種類のツールはリンターと呼ばれます。私はpython開発者ではありませんが、 Pylint のように見えますが、良いオプションかもしれません。

リンターは、作成したコードを調べ、構成可能な一連のベストプラクティスと比較します。リンターに変数がcamelCaseでなければならないというルールがあり、snake_caseに記述した場合、それは間違いとしてフラグが立てられます。優れたリンターには、「宣言された変数を使用する必要がある」から「関数の循環的複雑度は3未満でなければならない」までのルールがあります。

ほとんどのコードエディターは、保存するたびにリンターを実行するように、または一般的に入力時にリンターを実行し、インラインで問題を示すように構成できます。 x = 7のように入力すると、xが強調表示され、より長く、より適切な名前を使用するように指示されます(設定した場合)。これは、ほとんどのワードプロセッサのスペルチェックのように機能し、無視するのを難しくし、より良い習慣を構築するのに役立ちます。

5
Mike Gossmann

すでにここに書かれている良いアドバイスに加えて、プログラミングの目的、したがってあなたにとって何が重要かを検討する必要があるかもしれません。

「私がプログラミングでテストまたは研究しているのは、数学または科学についてです。」

目的が何かを自分で理解するために実験してテストすることであり、結果がどうなるかがわかっている場合、コードは基本的にすぐに使い捨てにすることができ、現在のアプローチで十分ですが、改善できる可能性があります。結果が期待どおりでない場合は、戻って確認できます。

ただし、コーディングの結果が研究の方向性を示しており、結果がどうあるべきかがわからない場合は、正確性が特に重要になります。コードのエラーは、実験から誤った結論を導き、全体的な研究にさまざまな悪い影響を与える可能性があります。

その場合、ユニットテストを使用してコードを簡単に理解および検証可能な関数に分割することで、より確かなビルディングブロックが得られ、結果の信頼性が高まり、後で多くのフラストレーションからあなたを救うことができます。

5
Aidan

これまでに提供されたすべての優れた提案に加えて、私が時間をかけて習得した本質的な1つの実践は、コードに非常に自由に詳細なコメントを追加することです。久しぶりに戻って来るとき、それは私にとって最も重要なことです。自分が何を考えているのか説明してください。少し時間がかかりますが、比較的簡単でほとんど痛みはありません。

特に概念や手法が新しく、自分自身で説明している場合は、コードの2〜3倍のコメント行があることがあります。

バージョン管理を行ったり、プラクティスを改善したりします。...上記すべて。しかし、あなたが行くときに物事を自分に説明してください。それは本当にうまくいきます。

4
Bob Newell

リストしたものはすべて、比喩的なツールボックスのツールです。人生のあらゆるものと同様に、さまざまなツールがさまざまなタスクに適しています。

他のエンジニアリング分野と比較すると、ソフトウェアはそれ自体が非常に単純な一連の個々の部分で機能します。割り当てステートメントは、部屋の温度変動に応じて異なる評価を行いません。 ifステートメントは腐食せず、しばらくすると同じものを返し続けます。しかし、個々の要素は非常にシンプルであり、ソフトウェアは人間が作成したものであるため、結果が非​​常に大きく複雑になり、人々が精神的に管理できる限界に達するまで、これらの要素はどんどん大きくなっていきます。

ソフトウェアプロジェクトが成長するにつれて、人々はそれらを研究し、その複雑さを管理するためのツールを作成しました。 OOPはその一例です。抽象的なプログラミング言語がますます増えていることは、もう1つの手段です。ソフトウェアのお金の多くがをやっているので、もっともっと、それを達成するためのツールは、あなたが見たり読んだりするものですが、それらの状況はあなたには当てはまらないようです。

だから、あなたのように感じないでください必要それをすること結局のところ、コードは目的を達成するための手段にすぎません。残念ながら、何が適切で何が不適切であるかについて正しい見方を与えるのに最適なのは、いくつかの大規模なプロジェクトで作業することです。

いずれにせよ、スクリプトが小さい限り、OOPまたはその他の手法を使用しないことについては心配しません。あなたが説明した問題の多くは、一般的な専門的な組織スキル、つまり古いファイルは、すべてのフィールドで処理する必要があるものです。

4
whatsisname

多くの(数学/科学)コードを書く学者たちと同じような環境で働いてきましたが、あなたが説明するのと同じ理由のために、彼らの進歩は遅いです。しかし、私はあなたを助けることができると私が思うにうまくいった1つの特定のことに気づきました:複数のプロジェクトで使用できる特殊なライブラリのコレクションを構築して維持します。これらのライブラリはユーティリティ関数を提供する必要があるため、問題のドメインに固有の現在のプロジェクトを維持するのに役立ちます。

たとえば、フィールドで多くの座標変換(ECEF、NED、緯度/経度、WGS84など)を処理する必要がある場合があるため、convert_ecef_to_ned()などの関数を新しいプロジェクトに入れる必要がありますCoordinateTransformationsと呼ばれます。プロジェクトをバージョン管理下に置き、部門のサーバーでホストして、他の人がそれを使用できるように(できれば改善できるように)します。その後、数年後には、プロジェクトに特定の問題/研究ドメインに固有のコードのみを含む、強力なライブラリのコレクションができるはずです。

より一般的なアドバイス:

  • 特定の問題が何であるかに関係なく、常にできるだけ正確にモデル化することを目指してください。そうすれば、変数をどこに、どこに、どのように置くかなどのソフトウェア設計の質問に答えるのがはるかに明白になるはずです。
  • 科学的なコードはアイデアと概念を記述し、より創造的で流動的であるため、テスト駆動開発に煩わされることはありません。定義するAPI、保守するサービス、機能を変更するときの他の人のコードへのリスクなどはありません。
4
jigglypuff

この種のプログラムにはどのような資質が重要ですか?

メンテナンスが簡単なのか、それが進化するのかは、おそらく問題ではありません。なぜなら、それは起こりそうもないことだからです。

それがどれほど効率的であるかはおそらく問題ではありません。

優れたユーザーインターフェイスを備えているかどうか、または悪意のある攻撃者に対して安全であるかどうかは、おそらく問題ではありません。

コードが読み取り可能であることが重要である場合があります。コードを読んでいる人は、自分が主張することを実行していることを簡単に納得させることができます。

それが正しいことは確かに重要です。プログラムが誤った結果を出す場合、それはあなたの科学的結論の枠を超えています。しかし、実際に処理するように要求している入力を正しく処理する必要があるだけです。すべてのデータ値が正である場合、負の入力データ値が与えられた場合にフォールオーバーするかどうかは、それほど重要ではありません。

また、ある程度の変更管理を維持することも重要です。科学的結果は再現可能である必要があります。つまり、公開する予定の結果を生成したプログラムのバージョンを知る必要があります。開発者は1人しかいないため、変更コントロールはそれほど複雑である必要はありませんが、特定の時点に戻って結果を再現できることを確認する必要があります。

したがって、プログラミングのパラダイム、オブジェクト指向、アルゴリズムの優雅さについて心配する必要はありません。わかりやすさと読みやすさ、および時間の経過に伴う変更の追跡可能性について心配してください。ユーザーインターフェースについて心配する必要はありません。入力パラメーターの可能なすべての組み合わせをテストすることについて心配する必要はありませんが、結果と結論が有効であることを確信できるように(そして他のユーザーを確信できるように)十分なテストを行ってください。

4
Michael Kay

以下は私の意見であり、私自身の特定の道に非常に影響を受けています。

コーディングは、多くの場合、どのようにすべきかについて独断的な見方を生み出します。テクニックとツールの代わりに、適切な戦略を決定するには、累積値とコストを調べる必要があると思います。

読みやすく、デバッグが可能で、しっかりしたコードを書くには、多くの時間と労力がかかります。多くの場合、計画期間が限られているため、これを行う価値はありません(分析麻痺)。

ある同僚には経験則がありました。基本的に同じ種類のことを3回目に行う場合は、努力を投資します。それ以外の場合は、迅速で汚い仕事が適切です。

ある種のテストは不可欠ですが、1回限りのプロジェクトでは、目を見張るだけで十分な場合があります。重要なことには、テストとテストインフラストラクチャが不可欠です。その価値は、コーディングの際にあなたを解放することです。コストは、テストが特定の実装に焦点を当てている場合、テストもメンテナンスを必要とすることです。テストはまた、物事がどのように動作することが想定されているかを思い出させます。

私自身の1回限りのスクリプト(確率の推定値の検証など)では、2つの小さなことが非常に役立つことがわかりました。1.コードの使用方法を示すコメントを含めます。 2.コードを作成した理由の簡単な説明を含めます。これらのことは、コードを書くときにはひどく明白ですが、明白さは時間とともに無駄になります:-)。

OOPは、コードの再利用、抽象化、カプセル化、因数分解などに関するものです。非常に便利ですが、高品質のコードとデザインを作成することが最終目標ではない場合、迷子になってしまいます。高品質なものを作るには時間と労力が必要です。

3
copper.hat

ユニットテストにはメリットがあると思いますが、それらは科学的発展にとって疑わしい価値があります。

しかし、私は科学的コードの統合テストが本当に好きです:

独自に機能するコードの小さなチャンクを分離します。 ETLパイプライン。次に、データを提供するテストを記述し、etlパイプライン(またはステップのみ)を実行して、結果が期待と一致することをテストします。テストされたチャンクは多くのコードになる可能性がありますが、テストはまだ価値を提供します:

  1. コードを再実行するための便利なフックがあり、頻繁に実行するのに役立ちます。
  2. テストでいくつかの仮定をテストできます
  3. 何かが壊れると、失敗したテストを追加して修正するのは簡単です
  4. 予想される入力/出力を体系化し、入力データ形式を推測しようとすることによる通常の頭痛を回避します。
  5. 単体テストほどリーンではありませんが、ITテストはコードを分解して、コードに境界を追加することを強制するのに役立ちます。

私はこの手法を頻繁に使用しており、多くの場合、比較的読みやすいメイン関数が使用されますが、サブ関数はかなり長くて見苦しいことが多いですが、堅牢なI/O境界があるため、すばやく変更して再配置できます。

3
Christian Sauer

私は通常、非常に大きなソースベースで作業します。私たちはあなたが言及するすべてのツールを使用します。最近、いくつかのpythonサイドプロジェクトのスクリプトを作成し始めました。これらは最大で数十行から数百行です。習慣から、スクリプトをソース管理にコミットしました。機能しない可能性のある実験を試すためのブランチを作成できるので、これは便利です。コードを複製して別の目的で変更する必要がある場合は、フォークできます。これにより、元のコードをそのままにして、再度使用する必要が生じます。 。

「単体テスト」の場合、手作業で確認する既知の出力を生成することを目的としたいくつかの入力ファイルがあります。おそらく自動化することはできますが、それを実行することで節約するよりも、実行するのに時間がかかるように感じます。おそらく、スクリプトを変更して実行する頻度に依存します。どちらにしても、それが機能する場合は、それを実行します。それが価値があるよりも面倒な場合は、時間を無駄にしないでください。

2
user1118321

コードを書くことで-一般的な書き込みと同様に-主な質問は:

どの読者を念頭に置いていますか?または誰があなたのコードを消費しますか?

正式なコーディングガイドラインのようなものは、あなたが唯一の対象者である場合には意味がありません。

そうは言っても、その一方で、あなたが将来あなたがそれをすぐに理解できるような方法でコードを書くことは役に立ちます。

そのため、「良いスタイル」が最も役立ちます。そのスタイルがどうあるべきかは私が与えることができない答えです。

OOPまたは150 LOCのファイルの単体テストは必要ないと思います。専用のVCSは、進化するコードがある場合に興味深いでしょう。それ以外の場合は.bakトリックを行います。これらのツールは、病気の治療法です。

おそらく、酔っている間にそれを読んでも、それを読んで理解し、変更できるような方法でコードを書くべきです。

2
Thomas Junk