多数のMATLABコードの所有権を取得し、コードに散らばっている「マジックナンバー」の束に気付きました。通常、C、Ruby、PHPなどの言語でこれらの定数を作成するのが好きです。この問題をグーグルで調べたとき、定数を持つ「公式な」方法は定数値を返す関数を定義することであることがわかりました。特に、ファイルごとに複数の関数を許可する場合、MATLABは細心の注意を払う可能性があるためです。
これは本当に最良の選択肢ですか?
私はこれを行うためにCプリプロセッサのようなものを使用/作成したいと思っています。 (mpp
と呼ばれるものが似たような苦境にある誰かによって作られたことがわかりましたが、見た目は放棄されているようです。コードはコンパイルされず、それが私のニーズを満たすかどうかわかりません。)
通常、UPPER_CASEで変数を定義し、ファイルの先頭近くに配置します。しかし、その価値を変えないという責任を負う必要があります。
それ以外の場合は、 MATLABクラスを使用 で名前付き定数を定義できます。
Matlabには現在定数があります。 Matlab OOPの新しい(R2008a +) "classdef"スタイルを使用すると、一定のクラスプロパティを定義できます。これはおそらく、古いMatlabとの後方互換性を必要としない場合に最適なオプションです。逆に、後方互換性を放棄する正当な理由です。)
クラスでそれらを定義します。
classdef MyConstants
properties (Constant = true)
SECONDS_PER_HOUR = 60*60;
DISTANCE_TO_MOON_KM = 384403;
end
end
次に、ドット修飾を使用して他のコードからそれらを参照します。
>> disp(MyConstants.SECONDS_PER_HOUR)
3600
詳細については、「ユーザーガイド」の「オブジェクト指向プログラミング」のMatlabドキュメントを参照してください。
いくつかのマイナーな落とし穴があります。コードがエラーを取得する代わりに誤って定数に書き込もうとすると、定数クラスをマスクするローカル構造体が作成されます。
>> MyConstants.SECONDS_PER_HOUR
ans =
3600
>> MyConstants.SECONDS_PER_HOUR = 42
MyConstants =
SECONDS_PER_HOUR: 42
>> whos
Name Size Bytes Class Attributes
MyConstants 1x1 132 struct
ans 1x1 8 double
しかし、被害は局所的です。また、徹底したい場合は、関数の先頭でMyConstants()コンストラクターを呼び出して、Matlabがそのスコープ内のクラス名として解析するように強制することで、これを防ぐことができます。 (私見はこれはやり過ぎですが、必要に応じてそこにあります。)
function broken_constant_use
MyConstants(); % "import" to protect assignment
MyConstants.SECONDS_PER_HOUR = 42 % this bug is a syntax error now
もう1つの落とし穴は、classdefのプロパティとメソッド、特にこのようなstaticが遅いことです。私のマシンでは、この定数の読み取りは、単純な関数を呼び出すよりも約100倍遅い(22 usec対0.2 usec、 この質問 を参照)。ループ内で定数を使用している場合、ループに入る前に定数をローカル変数にコピーします。何らかの理由で定数の直接アクセスを使用する必要がある場合は、値を返す単純な関数を使用してください。
あなたの正気のために、プリプロセッサのものから離れてください。 Matlab IDEとデバッガー(非常に便利です)内で動作させるには、深くて恐ろしいハックが必要です。
MATLABには、厳密なconstの同等物がありません。定数にグローバルを使用しないことをお勧めします。1つには、使用するすべての場所で宣言されていることを確認する必要があります。必要な値を返す関数を作成します。いくつかのアイデアについては、 このブログ投稿 をご覧ください。
これらの答えのいくつかがあります MATLABで列挙型を作成するにはどうすればよいですか 役に立ちます。しかし、要するに、MATLABでの初期設定後に値が変化してはならない変数を指定する「1行」の方法はありません。
いずれにせよ、それはまだややこしいものです。過去のプロジェクトでは、これに対する私のアプローチは、1つのスクリプトファイルですべての定数をグローバル変数として定義し、プログラム実行の開始時にスクリプトを呼び出して変数を初期化し、「global MYCONST;」を含めることでしたMYCONSTを使用する必要がある関数の先頭のステートメント。このアプローチが定数値を返す関数を定義する「公式の」方法より優れているかどうかは、どちらの方法でも議論できる意見の問題です。どちらの方法も理想的ではありません。
他の関数に渡したい定数を処理する私の方法は、構造体を使用することです。
% Define constants
params.PI = 3.1416;
params.SQRT2 = 1.414;
% Call a function which needs one or more of the constants
myFunction( params );
Cヘッダーファイルほどきれいではありませんが、ジョブを実行し、MATLABグローバルを回避します。定数をすべて別のファイル(たとえば、getConstants.m)で定義する場合、それも簡単です。
params = getConstants();
大文字で単純な定数を使用したスクリプトを使用し、他のスクリプトtr = that beedにスクリプトを含めます。
LEFT = 1;
DOWN = 2;
RIGHT = 3; etc.
これらが一定ではないことは気にしません。 「LEFT = 3」と書くと、私は愚かに愚かになり、とにかく愚かさに対する治療法はないので、気にしません。しかし、このメソッドは、検査する必要のない変数でワークスペースを混乱させるという事実を本当に嫌います。また、「turn(MyConstants.LEFT)」のようなものを使用するのも好きではありません。これにより、10億文字のような長いステートメントが作成され、コードが読みにくくなります。
私が必要とするのは変数ではなく、実際のプリコンパイラ定数を持つ可能性です。つまり、コードを実行する直前に値に置き換えられる文字列です。それはそうあるべきです。定数は変数である必要はありません。コードを読みやすく保守しやすくすることのみを目的としています。 MathWorks:どうぞ、どうぞ。これを実装するのはそれほど難しくありません。 。 。
最初にインスタンスを作成せずにmyClass.myconst
を使用して定数を呼び出さないでください!速度が問題にならない限り。私は、定数プロパティへの最初の呼び出しがインスタンスを作成し、その後のすべての呼び出しがそのインスタンスを参照するという印象を受けていました( 定数値を持つプロパティ )、しかし、場合。次の形式の非常に基本的なテスト関数を作成しました。
tic;
for n = 1:N
a = myObj.field;
end
t = toc;
クラスが次のように定義されている場合:
classdef TestObj
properties
field = 10;
end
end
または:
classdef TestHandleObj < handle
properties
field = 10;
end
end
または:
classdef TestConstant
properties (Constant)
field = 10;
end
end
オブジェクト、ハンドルオブジェクト、ネストされたオブジェクトなどのさまざまなケース(および割り当て操作)。これらはすべてスカラーであることに注意してください。配列、セル、または文字を調査しませんでした。 N = 1,000,000の場合、結果(合計経過時間)は次のとおりです。
Access(s) Assign(s) Type of object/call
0.0034 0.0042 'myObj.field'
0.0033 0.0042 'myStruct.field'
0.0034 0.0033 'myVar' //Plain old workspace evaluation
0.0033 0.0042 'myNestedObj.obj.field'
0.1581 0.3066 'myHandleObj.field'
0.1694 0.3124 'myNestedHandleObj.handleObj.field'
29.2161 - 'TestConstant.const' //Call directly to class(supposed to be faster)
0.0034 - 'myTestConstant.const' //Create an instance of TestConstant
0.0051 0.0078 'TestObj > methods' //This calls get and set methods that loop internally
0.1574 0.3053 'TestHandleObj > methods' //get and set methods (internal loop)
Javaクラスも作成し、同様のテストを実行しました。
12.18 17.53 'jObj.field > in matlab for loop'
0.0043 0.0039 'jObj.get and jObj.set loop N times internally'
Javaオブジェクトを呼び出す際のオーバーヘッドは高くなりますが、オブジェクト内では、通常のmatlabオブジェクトと同じ速さで単純なアクセスおよび割り当て操作が行われます。参照動作を起動する場合は、Javaを使用する方法があります。ネストされた関数内のオブジェクト呼び出しを調査しませんでしたが、奇妙なことを見てきました。また、プロファイラーはこのようなものの多くに関しては不要です。そのため、時間を手動で保存することに切り替えました。
参考のため、使用されたJavaクラス:
public class JtestObj {
public double field = 10;
public double getMe() {
double N = 1000000;
double val = 0;
for (int i = 1; i < N; i++) {
val = this.field;
}
return val;
}
public void setMe(double val) {
double N = 1000000;
for (int i = 1; i < N; i++){
this.field = val;
}
}
}
関連する注意事項として、NIST定数のテーブルへのリンク: ascii table およびリストされた値を持つ構造体を返すmatlab関数: Matlab FileExchange