痛い!痛い!あなたがこの道を進む前に、私はただそこに飛び込んであなたを邪魔させてください!
あなたがプログラマーではないことは知っていますが、人生のある時点で(明らかに、これはあなたのものです!)、事実に直面し、瞬間的に1つになる必要があります。ですから、プログラミングは実際には科学ではなく、芸術であり、必要に応じて職人技であり、間違いを犯しやすいものであることを知っておいてください。また、あなたの前に何百万人ものプログラマーがいて、あなたのために道を開いて、どの方法が最も効果的で、どの方法が特定の災害につながるかを見つけたということも知っておいてください。
コードに存在するこれらの「特定の運命への道」のうちの6つについて説明します。
リストの最初global
は、の使用です。グローバル変数を使用しないでください!! 確かに、それらは小さくて単純なものには問題ありませんが、データを渡すためのより良い、はるかに管理しやすく、より耐久性があり、堅牢で、エラーが発生しにくい方法は、手動で行うことです。経験則として、他の関数/変数への依存関係をできるだけ少なくして、すべてのトップレベル関数を作成します。これは、グローバル変数がプログラムの状態と関数の出力の間に緊密な結合を作成するためです。これにより、エラーの再現が不可能ではないにしても困難になり、デバッグ(実際にはプログラマーがほとんどの時間を費やすもの)が完全な悪夢になります。 。また、実行中の関数以外の関数はそれらを変更できるため、
function testMe
global a;
a = 5*rand;
someFunction;
b = 4*a; % ERROR! or...will it?
function someFunction
global a;
a = a/5;
if a < 0.5
someOtherFunction; end
function someOtherFunction;
global a;
a = {'my string'};
動作することもあれば、失敗することもあります。発生する可能性のあるさらに悪い例:
function testMe
global a, b;
a = 5; b = 6;
result = someCalculation;
result = a*b*result;
function someFunction
global a;
a = sin(pi/rand); % INTENTIONAL
% do LOTS of stuff here
for a = 1:10 % OOPS! unintentional use of variable name
% do stuff
if (some weird condition)
break; end
end
エラー、警告、何もありませんが、結果はごみになります。そして、関数が大きくなるにつれて(そして、通常はそうなるでしょう)、このエラーはますます難しくなり、見つけるのが難しくなります。この種の間違いを見つけるのに数日を費やすことは珍しいことではありません。
コードでは、グローバル変数a
とb
ループ内も変更します。これは、a
andを使用する関数/スクリプトは、これが完了した後b
に呼び出されると、 andを参照することを意味します。ここで、これらのループ内で、の値を変更する関数を呼び出すとします。-loopの次の反復での値はどうなりますか?また、誤った結果が得られたとします。そのエラーをどのように見つけますか?a=10
b=10
a
a
a
このようなコードは、明らかな理由から、通常「スパゲッティコード」と呼ばれます。おそらくそれは機能し、コーディングは簡単ですが、最終的には常に非常に遅くなります(コードを継承する人は言うまでもありません)。
これのほとんどを防ぐはるかに優れたアプローチは、より大きなコンテナーにデータを収集し、それらを明示的に渡すことです。struct
データにを使用するとしますa-l
:
data = struct(...
'a', a,...
'b', b,...
'c', c,...
'd', d,...
'e', e,...
'f', f,...
'g', g,...
'h', h,...
'l', l);
あなたが言うことができるように
result = myFunction(data);
内部のデータへのアクセスmyFunction
は次のようになります。、、またはのdata.a
値などを取得します。と言っても、、または関数に渡された元の値は変更されません。密結合が解除され、前述のすべての問題が回避されました。a
data.f
f
data.k = 5;
myFunction
result
data
これらの種類の汎用コンテナーについて学習するには、Matlabコマンドウィンドウでまたはと入力しますhelp struct
。help cell
リストの2番目l
は、変数名を使用しています。それはややばかげています、そして私はこれについて短くすることができます:それをしないでください:)ほとんどの人(そして一部のプログラマーさえ)が信じていることに反して、あなたはコードの行を一度だけ書きます、しかしあなたはそれを数千とまではいかなくても数百を読みますの時間。ベストプラクティスは、書き込みではなく、読み取りをできるだけ簡単にすることです。のように見えますね。バグvsは、 vsよりも簡単に見つけるのが困難です。l
1
k=1
k=l
k=m
k=1
リストの3番目transpose
はキーワードです。ちょっと冗長ですね。数学では、A Tを使用します。これは、常に完全な定義を記述するよりも、目にははるかに簡単です。
B={ Aij➝Aji∀i < m⋏j <n
通常、B =ATとだけ言います。Matlabでも同じです。行列のtranspose
は次のように実行できます。
Actrans = A' ; % conjugate transpose
Atrans = A.'; % regular transpose
これにより、コードがはるかに冗長になります
A = [1 3;3 2];
B = [a 0;0 b];
C = [d 0;e f];
D = [sqrt(d) 0;0 sqrt(f)];
E = C.'/D;
K = A+E;
M = E*D*E.';
リストの4番目K==M
は平等です。ここにあるように、そしてK
行列です。式は、プログラミングのキャリアの後半で明らかになる理由により、要素ごとに評価されます:)これは、とと同じサイズの行列になり、対応する要素が等しくない場合、およびこれらの要素が含まれる場合を含みます。は同じ。では、ステートメントはそのような行列で何をするのでしょうか?Matlabでは、最初の要素が真である場合は常にそうなります(私の意見では、エラーがスローされるはずですが、まあまあ)。M
K==M
K==M
K
M
0
K
M
1
if
true
これは明らかにあなたが望むものではありません。私が望むのは、両方の行列のすべての要素が等しいということです。これを使用するのが最善です:
if all( abs(K(:)-M(:)) < eps )
ここで、(:)
-notationは行列K
を意味しM
、比較の前に列ベクトルに展開する必要があります。これはall()
、単一の次元で機能するためall(K==M)
、行列のままであるためです(実際にはベクトルですが、同じものの特殊なケースでは別の名前です)。等式(==
)を使用せず、その差が小さな値(eps
)よりも小さいかどうかを確認することに注意してください。これは、浮動小数点演算(すべてのコンピューターが使用)では、乗算や平方根などの演算は通常、丸め誤差や近似/補間誤差などの問題に悩まされるためです。平等は非常に厳しい要求であり、評価するには難しすぎるtrue
ほとんどの場合、数学的に言えばそうすべきです。2つの差を丸め誤差()に関連する小さな値と比較することにより、この等価性の検出の失敗を防ぐことができますeps
。
リストの5番目は、印刷方法です。ステートメント自体は、システムのデフォルトプリンターに図を送信します。今日、協力しているように感じる場合は、インクが付いた紙を吐き出す不機嫌そうなprint
マシンです:)さて、あなたは画面。物事を表示するために設定した方法のようにそれを行うことは最善の方法ではありません。名前のない、構造化されていない値のこのリストの数十倍を取得します。
1 % which would be the value of 'a'
1 % which would be the value of 'b'
3 % which would be the value of 'd'
4 % which would be the value of 'e'
5 % which would be the value of 'f'
...
値だけが表示されるのを見ると、何が起こっているのかを読んで解釈するのがかなり面倒になります。より説明的なものを使用することをお勧めします。
if all( abs(K(:)-M(:)) < eps )
% option 1
a
b
d % NOTE: not terminating with semicolon
e
f
% option 2
fprintf(...
'a: %d\n, b: %d\n, d: %d\n, e: %d\n, f: %d\n\n', a,b,d,e,f);
end
オプション1は表示されます
a =
1
b =
1
etc.
これは、少なくとも変数の名前とその値も表示します。オプション2はより良いものです:
a: 1
b: 1
d: 3
e: 4
f: 5
a: 1
b: 2
d: 3
e: 4
f: 5
etc.
(余談ですが、ループ内で値a,b,d,e,f
が変わることはないので、そもそもなぜそれらを表示したいのですか?)
リストの6番目for
(そして最後!)は、Matlabに固有のものです: -loops。Matlabは、解釈されたマトリックスベースの言語です。その行列の性質は、すべての変数が本質的に行列であることを意味します。インタプリタとは、コードがコンピュータのプロセッサに直接表示されないことを意味します。何かが計算される前に、一連の解釈と変換を行う必要があります。このコインには2つの側面があります。
- それは物事をスピードアップすることができます(コーディング、線形システムの解法、FFT、行列の比較などの「些細な」ことを行うなど)
- それは物事を遅くする可能性があります(ループのように、ステートメントの繰り返し実行のように)
パフォーマンスの観点からfor
、Matlabでは-loopは操作をクロールすることで有名です。Matlabに入る方法は通常、ベクトル化されたコードです。たとえば、すべての変数が行列であるという事実を使用し、ループの代わりにそれらに対して行列/テンソル演算を使用します。これはほとんどのプログラミング言語ではあまり一般的なアプローチではありませんが(それに慣れていないプログラマーには、これに対する強い抵抗がたくさん見られます)、数学的な文脈では非常に理にかなっています。forループを使用する前に、常に行列/テンソル演算を攻撃の最初の行として使用するようにしてください(Matlabには多くの演算があります)。
だから、それはあなたのコードの何が悪いのか:)そうそう、そしてアンドレアス・ハンガウアーがすでに述べたように、a
スルーを参照するステートメントを配置しl
、ループ内でそれに応じて再計算する必要があるすべてのものを配置すれば、問題ありません。