MATLAB 中是否有 foreach 循环?如果有,当基础数据发生变化时它会如何表现?

180

在MATLAB中,是否有foreach结构?如果有,如果基础数据发生变化(例如,如果向集合中添加对象),会发生什么?

9个回答

151
MATLAB的FOR循环是静态的;在迭代之间,您无法修改循环变量,这与其他语言中的for(initialization;condition;increment)循环结构不同。这意味着以下代码始终打印1、2、3、4、5,而不管B的值如何。
A = 1:5;

for i = A
    A = B;
    disp(i);
end

如果您希望在迭代过程中能够响应数据结构的更改,WHILE 循环可能更加合适 --- 您将能够在每次迭代时测试循环条件,并根据需要设置循环变量的值:

n = 10;
f = n;
while n > 1
    n = n-1;
    f = f*n;
end
disp(['n! = ' num2str(f)])

顺便提一下,在迭代过程中修改数据结构时,Java(以及可能其他语言)的for-each循环会产生未指定的行为。如果需要修改数据结构,应该使用适当的Iterator实例,允许在正在迭代的集合中添加和删除元素。好消息是MATLAB支持Java对象,因此您可以像这样做:

A = java.util.ArrayList();
A.add(1);
A.add(2);
A.add(3);
A.add(4);
A.add(5);

itr = A.listIterator();

while itr.hasNext()

    k = itr.next();
    disp(k);

    % modify data structure while iterating
    itr.remove();
    itr.add(k);

end

1
如果B未定义,你的第一个例子不会打印1-5。它会打印“未定义函数或变量'B'”。 - Kleist
3
对于第一个例子,请确保A是一行向量,而不是列向量。如果A是一个矩阵,那么每个k将会是该矩阵的一个列向量。因此,如果需要的话,请转置(A')或向量化(A(:)')。 - yuk
3
我认为在.m文件中使用类似Java的代码不应该是你首选的方式来处理Matlab。 - bobobobo
1
来自未来的问候;我们带来了大量解决迭代器失效问题的方案。 - Dmytro

96

关于问题的直接回答,Zach是正确的。

有趣的是,以下两个循环并不执行相同的操作:

for i=1:10000
  % do something
end
for i=[1:10000]
  % do something
end

第一个循环创建了一个标量变量 i,它的迭代方式类似于 C 的 for 循环。请注意,如果您在循环体内修改 i,则修改后的值将被忽略,如 Zach 所述。第二种情况下,Matlab 创建一个包含10k个元素的数组,然后遍历该数组的所有元素。

这意味着什么是,

for i=1:inf
  % do something
end

功能正常,但

for i=[1:inf]
  % do something
end

这个操作不可行(因为这需要分配无限的内存)。详情请参见Loren的博客

此外,请注意您可以遍历单元数组。


2
是的,当我遇到这个问题时,我也感到惊讶。实际上,在许多地方都进行了数组优化。如果您使用括号表示法,有时您会在Matlab编辑器中看到性能警告,告诉您它认为如果您允许它,它可以优化掉数组分配。 - Mr Fooz
我听说Matlab现在有惰性求值。如果没有,我们确实有技术来实现它们。 - Dmytro

21

MATLAB的for循环基本上允许极大的灵活性,包括功能。以下是一些示例:

1)定义开始、增量和结束索引

for test = 1:3:9
   test
end

2) 循环遍历向量

for test = [1, 3, 4]
   test
end

3) 循环字符串

for test = 'hello'
   test
end

4) 循环遍历一维单元数组

for test = {'hello', 42, datestr(now) ,1:3}
   test
end

5)循环遍历一个二维细胞数组

for test = {'hello',42,datestr(now) ; 'world',43,datestr(now+1)}
   test(1)   
   test(2)
   disp('---')
end

6) 使用结构数组的字段名

s.a = 1:3 ; s.b = 10  ; 
for test = fieldnames(s)'
   s.(cell2mat(test))
end

5
注意,使用cell数组时,它会按列迭代 - Evgeni Sergeev

19

如果您正在尝试循环遍历一个单元数组并对每个元素应用某些操作,请查看cellfun。此外还有arrayfunbsxfunstructfun可以简化您的程序。


尽管从我的经验来看,它们的性能与编写for循环相当或更差,但外观更好,而且谁知道它们未来可能会改善。 - user677656

15

哦!很棒的问题。

Matlab 的 for 循环输入一个矩阵,并迭代其列。 Matlab 也通过值处理几乎所有内容(不支持传递引用),因此我期望它会对 for 循环的输入进行快照,使其成为不可变的。

下面是一个可能有助于说明的示例:

>> A = zeros(4); A(:) = 1:16

A =

     1     5     9    13
     2     6    10    14
     3     7    11    15
     4     8    12    16

>> i = 1; for col = A; disp(col'); A(:,i) = i; i = i + 1; end;
     1     2     3     4

     5     6     7     8

     9    10    11    12

    13    14    15    16

>> A

A =

     1     2     3     4
     1     2     3     4
     1     2     3     4
     1     2     3     4

7

当迭代字符串的单元格数组时,循环变量(我们称之为f)变成了一个单元格数组。到处写f{1}变得繁琐,修改循环变量提供了一种简洁的解决方法。

% This example transposes each field of a struct.
s.a = 1:3;
s.b = zeros(2,3);
s % a: [1 2 3]; b: [2x3 double]
for f = fieldnames(s)'
    s.(f{1}) = s.(f{1})';
end
s % a: [3x1 double]; b: [3x2 double]

% Redefining f simplifies the indexing.
for f = fieldnames(s)'
    f = f{1};
    s.(f) = s.(f)';
end
s % back to a: [1 2 3]; b: [2x3 double]

5
假设您有一个数据数组:
n = [1    2   3   4   6   12  18  51  69  81  ]

然后你可以像这样“foreach”它:
for i = n, i, end

这将回显n中的每个元素(当然,替换i为更有趣的内容也是可能的!)

4
截至今天(2月27日),MATLAB文件交换平台上有一个新的For-Each工具箱,可以实现foreach的概念。虽然foreach不是MATLAB语言的一部分,但使用这个工具箱可以模拟foreach的功能。请点击链接查看更多信息。

4

我认为这是原帖作者真正想要的:

array = -1:0.1:10

for i=1:numel(array)
    disp(array(i))
end

这只会打印出10,因为numel(array)返回的是数组中元素的数量。也许你想要的是1:numel(array) - Kleist
for i = -1:0.1:10; disp(i); end; 这样不是更好吗? - Oriol

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接