它们是一样的。在 Matlab 的特定情况下,不要担心:将length() 或任何其他函数放在for 初始化子句中总是与在循环外评估它一样快,因为for 只会调用它一次无论哪种方式。您的直觉可能是基于其他一些语言的 for 循环,例如 C 和 Java,它们具有不同的行为。
根据定义,Matlab for 循环仅在循环开始时对其参数表达式求值一次,以预先计算循环索引变量 (i) 的值范围或数组以在内部执行循环通过。与许多其他语言不同,Matlab 的for 不会在每次循环时重新评估某些循环控制语句。 (这也是为什么在 Matlab for 循环的主体内分配循环索引变量没有效果,而在 C 或 Java 中,它将允许您“跳来跳去”并改变控制流。)
阅读 Matlab for documentation。它可能会更明确,但您会注意到它是根据表达式解析为的值定义的,而不是表达式本身。
语法等价
一个 C for 循环被定义为具有此行为。
/* 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 循环,因为每次循环迭代的循环索引变量的值在循环开始之前是已知的,并且与执行无关任何循环通过。
演示
您可以通过在循环控制子句中使用具有可观察到的副作用的函数来自己确认此行为。
function show_for_behavior
for i = 1:three(NaN)
disp(i);
end
function out = three(x)
disp('three() got called');
out = 3;
您可以看到整个循环只有一次调用。
>> show_for_behavior
three() got called
1
2
3
语言原因
这是我推测的地方。
除了方便之外,我怀疑 Matlab 以它的方式定义其 for 循环的原因之一,而不是在常规 while 循环上为您提供 C 风格的语法糖,是因为获取索引变量很棘手由于浮点舍入。默认情况下,您使用的数字循环变量是双精度数,对于较大的 x(大约 10^15)值,x + 1 == x,因为 x (eps(x)) 处的相对精度大于 1。
因此,如果您像这样对for i = A:B ... end 进行天真的while-loop 转换,您将有一个无限循环,因为在每一步,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