6

MATLAB でベクトルを反復処理する場合、多くの場合があります。

だから私はこれをします:

len = length(vector)
for i=1:len
 do_something();
end

length()しかし、これは「毎回関数を呼び出さないようにする」という単なる直感です。本当ですか?それとも、時間要件に関して、これと同じことですか:

for i=1:length(vector)
 do_something();
end

助けてくれてありがとう!

4

3 に答える 3

7

パフォーマンスや速度が気になる場合は、心配する必要はありません。それほど大きな違いはありません。@Davidは、これを裏付ける彼の回答でいくつかのタイミングを示しています。

美学の観点から、それが価値があるもののために、私は通常書くでしょう

for i = 1:numel(v)
    doSomething(v(i))
end

最初の次元の長さではなく、最長の配列次元の長さを与えるnumelのでlength、私は を好む傾向があります。lengthそれがベクトルであると常に確信している場合、それらは同じですが、私はその仮定を避けようとしています.

もちろん、numel(v)個別にアクセスする必要がある場合は、それを中間変数に抽出しvNumfor 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コロン演算子は , と同じではなく、同じ答えは得られないことを覚えておいてください。

于 2014-05-15T13:28:15.220 に答える
1

彼らは同じです。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
于 2014-05-15T23:50:04.570 に答える