MATLAB でベクトルを反復処理する場合、多くの場合があります。
だから私はこれをします:
len = length(vector)
for i=1:len
do_something();
end
length()
しかし、これは「毎回関数を呼び出さないようにする」という単なる直感です。本当ですか?それとも、時間要件に関して、これと同じことですか:
for i=1:length(vector)
do_something();
end
助けてくれてありがとう!
MATLAB でベクトルを反復処理する場合、多くの場合があります。
だから私はこれをします:
len = length(vector)
for i=1:len
do_something();
end
length()
しかし、これは「毎回関数を呼び出さないようにする」という単なる直感です。本当ですか?それとも、時間要件に関して、これと同じことですか:
for i=1:length(vector)
do_something();
end
助けてくれてありがとう!
パフォーマンスや速度が気になる場合は、心配する必要はありません。それほど大きな違いはありません。@Davidは、これを裏付ける彼の回答でいくつかのタイミングを示しています。
美学の観点から、それが価値があるもののために、私は通常書くでしょう
for i = 1:numel(v)
doSomething(v(i))
end
最初の次元の長さではなく、最長の配列次元の長さを与えるnumel
のでlength
、私は を好む傾向があります。length
それがベクトルであると常に確信している場合、それらは同じですが、私はその仮定を避けようとしています.
もちろん、numel(v)
個別にアクセスする必要がある場合は、それを中間変数に抽出しvNum
てfor i = 1:vNum
.
ループ インデックスのセマンティクスに関心がある場合for
、それは思ったよりも複雑です。を記述するときfor i = 1:N
、MATLAB は必ずしも単純にベクトル1:N
を作成してから反復処理するとは限りません。たとえば、次のように記述できます。
for i = 1:realmax
i
end
また
for i = 1:Inf
i
end
あるいは
for i = Inf:-1:1
i
end
そしてそれは機能します(押すCtrl-Cとエスケープします)。このような場合、MATLAB はループ インデックスをベクトルとして作成しません。また、大きすぎるため作成できませんでした。
パーサーにはそれよりも複雑なロジックがあり、いくつかのエッジ ケースと最適化の知識があります。ループの前にループ インデックスを作成することもあれば、その場で作成することもあります。MATLAB ソース コードにアクセスできない限り、すべての内部構造を推測することはできません。
また、ループ インデックスはベクトルである必要はありません。配列を指定すると、MATLAB は列をループします。例えば、
for i = magic(3)
i
end
配列の列を順番に表示します。同様に、セル配列を指定すると、セルを反復処理します (注: インデックスはセルであり、セル内の要素ではありません)。
このため、私は時々書くでしょう
for vElement = v
doSomething(vElement)
end
を使用した上記の最初のパターンではなくnumel
。
もちろん、アプリケーションによっては、ベクトル化doSomething
して を呼び出すほうがよい場合もありますdoSomething(v)
。
最後にもう 1 つ - ここまで整数インデックスについて実際に話してきましたが、コロン演算子はpi:sqrt(2):10
;のように任意のインクリメントを持つことができることを覚えておいてください。linspace
コロン演算子は , と同じではなく、同じ答えは得られないことを覚えておいてください。
彼らは同じです。Matlab の特定のケースでは、心配する必要はありません。初期化句に配置length()
する関数やその他の関数は、for
常にループ外で評価するのと同じくらい高速for
です。どちらの方法でも一度しか呼び出されないためです。あなたの直感はfor
、C や Java など、動作が異なる他の言語のループに基づいている可能性があります。
定義により、Matlabfor
ループは引数式をループの開始時に 1 回だけ評価し、ループ インデックス変数 ( i
) の値の範囲または配列を事前に計算して、ループ パス内で実行します。他の多くの言語とは異なり、Matlab の言語for
は、ループのたびにループ制御ステートメントの一部を再評価しません。(これが、Matlab ループの本体内でループ インデックス変数に代入しfor
ても効果がない理由でもあります。C または Java では、「ジャンプ アラウンド」して制御フローを変更することができます。)
ドキュメントについては、Matlabを参照してください。それについてはもっと明確にすることもできますが、式自体ではなく、式が解決される値の観点から定義されていることに気付くでしょう。
ACfor
ループは、この動作を持つように定義されています。
/* C-style for loop */
for ( A; B; C; ) {
...
}
/* basically equivalent to: */
{
A;
while ( B ) {
....
C;
}
}
機能的には、Matlab のfor
ループ構文の同等性はこれに似ています。
% Matlab for loop
for i = A:B
...
end
% basically functionally equivalent to:
tmp_X = A:B; % A and B only get evaluated outside the loop!
tmp_i = 1; % tmp_* = an implicit variable you have no access to
while tmp_i < size(tmp_X,2)
i = tmp_X(:,tmp_i);
...
tmp_i = tmp_i + 1;
end
そして実際には、Matlab はtmp_X
、プリミティブ値の場合に具体的な配列の作成を最適化できます。この制御式からのループ本体の分離はparfor
、Parallel Computing Toolbox で使用される並列ループのサポートにも役立ちます。これは、すべてのループ反復のループ インデックス変数の値が、ループの開始前に既知であり、いずれかの実行とは無関係であるためです。ループが通過します。
ループ制御句で観察可能な副作用を持つ関数を使用して、この動作を自分で確認できます。
function show_for_behavior
for i = 1:three(NaN)
disp(i);
end
function out = three(x)
disp('three() got called');
out = 3;
ループ全体で呼び出しが 1 つしかないことがわかります。
>> show_for_behavior
three() got called
1
2
3
ここで、少し推測します。
for
利便性を超えて、通常のループに C スタイルの構文糖衣を提供する代わりに、Matlab がそのループをそのように定義する理由の 1 つは、while
浮動小数点の丸めのためにインデックス変数を正しく取得するのが難しいためだと思います。デフォルトでは、使用している数値ループ変数は double であり、大きな値x
(約 10^15) の場合x + 1 == x
は x ( eps(x)
) での相対精度が 1 より大きいためです。
したがって、このように単純なwhile
ループ変換を行うfor i = A:B ... end
と、無限ループが発生します。これは、各ステップで、丸めによりi = i + 1
同じ値になるためです。i
i = A;
while (i < B)
...
i = i + 1;
end
大きな値のシーケンスに対してループを実行できるようにするには、値の範囲とステップ数を計算し、別の整数値を使用してループ インデックスを追跡し、i
そのカウンターとステップ サイズを使用して各ステップの値を構築します。各パスで一時変数をインクリメントする代わりに。このようなもの。
% original
for x = A:S:B; ...; end
% equivalent
nSteps = int64( ((B - A) / S) ) + int64(1);
i = int64(0);
while i < nSteps
x = A + (S * double(i));
....
i = i + int64(1);
end
これを行うことができるのは、最小、最大、およびステップの範囲がすべてのパスに対して事前に定義されている場合のみです。これは、より柔軟なwhile
-loop 形式では保証されません。
この場合、大きな A と B の場合、x は複数の反復パスでまったく同じ値になる可能性がありますが、最終的には進行し、無限精度を使用していた場合に予想される数のループ反復が得られることに注意してください。近似浮動小数点値の代わりに値。これは、これらの場合にMatlabが内部的に行うことに関するものだと思います。
この動作を示す例を次に示します。
function big_for_loop(a)
if nargin < 1; a = 1e20; end
b = a + 4 * eps(a);
step = 15;
fprintf('b - a = %f\n', b - a);
fprintf('1 + (b - a) / step = %f\n', 1 + (b - a) / step);
last_i = a;
n = 0;
for i = a : step : b
n = n + 1;
if (i ~= last_i); disp('i advanced'); end
last_i = i;
end
fprintf('niters = %d\n', n);
eps
これを実行すると、Matlab がループを実行する方法に基づいて、期待どおりに変化します。
>> big_for_loop
b - a = 65536.000000
1 + (b - a) / step = 4370.066667
i advanced
i advanced
i advanced
i advanced
niters = 4370