【问题标题】:Idiomatic way of exiting from multiple nested loops?从多个嵌套循环中退出的惯用方式?
【发布时间】:2015-10-10 16:09:27
【问题描述】:

MATLAB documentation 描述了 break 关键字:

  • break 终止 for 或 while 循环的执行。 break 语句之后的循环中的语句不执行。
  • 在嵌套循环中,break 仅从它发生的循环中退出。控制权传递给循环结束后的语句。

(我的重点)

如果你想退出多个嵌套循环怎么办?其他语言(例如 Java)提供 labelled breaks,允许您指定控制流的传输位置,但 MATLAB 缺少这种机制。

考虑以下示例:

% assume A to be a 2D array

% nested 'for' loops
for j = 1 : n
  for i = 1 : m
    if f(A(i, j)) % where f is a predicate
      break; % if want to break from both loops, not just the inner one
    else
      % do something interesting with A
    end
  end
  % <--- the break transfers control to here...
end
% <--- ... but I want to transfer control to here

退出两个循环的惯用方式(在 MATLAB 中)是什么?

【问题讨论】:

  • 我只是提取一个函数并从中返回
  • 您需要使用其他机制来退出所有嵌套循环。例如。添加更多 if 或将整个循环结构传递给函数,然后使用 return 而不是 break
  • 一个“惯用的”答案将取决于案件的具体情况。例如,您当前的示例根本不应该是一个嵌套循环,但使用线性索引会更好地执行,然后您只需要一个 break
  • 好吧,那么你的答案是“没有办法”。您正在寻找的东西可能在 Java 中是可能的,但老实说,这听起来很像变相的 goto。我看不到每种编程语言中如何“需要存在”该功能。在 Matlab 中,您将避免以其他方式需要该功能。通常这将向量化您的代码。由于矢量化您的示例很容易,这听起来像是一个计划。

标签: matlab nested-loops break labelled-break


【解决方案1】:

对于您的原始 特定示例,我会说,而不是使用线性索引和单个循环:

%// sample-data generation
m = 4;
n = 5;
A = rand(m, n);
temp = 0;

for k = 1:numel(A)
    if A(k) > 0.8 %// note that if you had switched your inner and outer loops you would have had to transpose A first as Matlab uses column-major indexing
      break; 
    else
      temp = temp + A(k);
    end
end

或实际上相同(但分支较少):

for k = 1:numel(A)
    if A(k) <= 0.8 %// note that if you had switched your inner and outer loops you would have had to transpose A first as Matlab uses column-major indexing
      temp = temp + A(k);
    end
end

我认为这个答案会因情况而异,并且没有通用的一种尺寸适合所有惯用正确的解决方案,但我会根据您的问题以下列方式处理它(注意这些都假设矢量化解决方案不实用,因为这是显而易见的首选)

  1. 减少嵌套的尺寸,不使用breaks 或只使用一个break(即如上所示)。
  2. Don't use break at all 因为除非你的谓词计算很昂贵并且你的循环有很多迭代,否则最后那些额外的迭代实际上应该是免费的。
  3. 失败了set a flag and break at each level
  4. 或者最后将你的循环包装成一个函数并调用return而不是break

【讨论】:

  • 谢谢,但不要专注于我的例子(我已经概括了)。我只是在寻找一种从多个嵌套循环中退出的通用方法,它可以随着嵌套级别很好地扩展。
  • @Jubobs 没有唯一正确的方法。我现在按优先顺序在我的答案中添加了 4 种方法。
【解决方案2】:

据我所知,没有内置这样的功能。但是,在大多数情况下,matlab 不需要嵌套循环,因为它支持矢量化。在向量化不起作用的情况下,循环大多是长而复杂的,因此多次中断不会显着影响可读性。如评论中所述,您实际上并不需要嵌套循环。矢量化可以解决问题,

m = 5;
n=4;
x = rand(m,n);
tmp = find(x>0.8, 1, 'first');
if (isempty(tmp))
   tmp = m*n+1;
end
tmp = tmp-1;
tot = sum(x(1:tmp));

当然可能有人声称 for 循环不再一定很慢,但事实仍然是 Matlab 的列很重,并且在大多数情况下使用多个循环将包括在非最佳维度上循环。矢量化解决方案不需要这样做,因为它们可以使用避免此类循环的智能方法(如果输入是行向量,这当然不成立,因此避免这种情况也很好)。

【讨论】:

    【解决方案3】:

    使用 Python(或您选择的毒药)的最佳惯用方式并忘记这一切,但这是另一回事。此外,我不再同意其他答案的矢量化声明。最近的 matlab 版本可以很快地处理 for 循环。你可能会感到惊讶。

    我个人的偏好是故意引发异常并将其包含在 try 和 catch 块中。

    % assume A to be a 2D array
    A = rand(10) - 0.5;
    A(3,2) = 0;
    
    wreaker = MException('Loop:breaker','Breaking the law');
    
    try
        for j = 1 : size(A,1)
            % forloop number 1
            for i = 1 : size(A,2)
                % forloop number 2
                for k = 1:10
                    % forloop number 3
                    if k == 5 && j == 3 && i == 6
                        mycurrentval = 5;
                        throw(wreaker)
                    end
                end
            end
        end
    catch
        return % I don't remember the do nothing keyword for matlab apparently
    end
    

    您可以更改 try catch 缩进的位置以退回到您选择的循环。此外,通过杀死小猫,您可以编写自己的异常,以便它们根据巢数标记异常,然后您可以监听它们。在我看来,尽管仍然比带有 if 子句的计数器或自定义变量更漂亮,但丑陋无止境。

    请注意,这正是 matlab 让很多人发疯的原因。它以非常相似的方式默默地抛出异常,并且您在经过时会为最后一个随机选择的函数得到一个无意义的错误,例如某些微分方程求解器中的大小不匹配等等。在阅读了很多 matlab 工具箱源代码后,我实际上学到了所有这些东西。

    【讨论】:

    • 好答案。鉴于您对 MATLAB 的仇恨,我很惊讶地看到您对 MATLAB 的回答:)
    • @Jubobs 我小时候需要钱所以我用了matlab
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-16
    • 2013-09-11
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    相关资源
    最近更新 更多