for int idx = 0; idx < ve"/>

关于Java:重构标记循环

关于Java:重构标记循环

Refactoring away labeled loops

在我确信标记的中断/继续完全是"麻烦"之后,我需要帮助将标记从代码中删除。

我有一个正方形矩阵和一个具有相同长度的向量。 向量中已经有一些值,取决于矩阵在循环中更改的值。

我希望代码片段基本上是可以理解的……

1
2
3
4
5
6
7
8
9
10
11
vectorLoop:
for( int idx = 0; idx < vectorLength; idx++) {
    if( conditionAtVectorPosition( v, idx ) ) continue vectorLoop;

    matrixLoop:
    for( rowIdx = 0; rowIdx < n; rowIdx++ ) {
        if( anotherConditionAtVector( v, rowIdx ) ) continue matrixLoop;
        if( conditionAtMatrixRowCol( m, rowIdx, idx ) ) continue vectorLoop;
    }
    setValueInVector( v, idx );
}

请说服我,这是一个没有标签的更具可读性/更好的版本。


看到目前为止提出的解决方案:

  • 它们看起来都比原始的可读性差,因为它们涉及在代码的机制上而不是算法本身上花费更多的代码。

  • 其中一些已损坏,或者已被编辑。最可恶的事实是,人们不得不认真思考如何在没有标签的情况下编写代码,而不破坏任何内容。

  • 有些测试会导致两次运行相同的测试而导致性能下降,但这并不总是微不足道的。替代方法是存储和传递布尔值,这变得很丑陋。

  • 将代码的相关部分重构为一种方法实际上是一种无操作的操作:它重新排列了代码在文件中的布局方式,但对代码的执行方式没有影响。

所有这些都使我相信,至少在提到这个问题的情况下,标签是正确的解决方案,不需要重构。当然,在某些情况下标签使用不正确,应将其重构。我只是认为不应将其视为牢不可破的规则。


我认为标记循环非常罕见,您可以选择任何适合您的标记方法-那里的内容使您的意图变得更加清晰。

在领导提出建议以重构原始问题中的循环并看到有问题的代码之后,我认为您那里的循环非常可读。

我想象的是一个截然不同的代码块-提出实际的示例,我发现它比我想象的要干净得多。

对于造成的误解,我深表歉意。


这个问题不是关于优化算法的-但还是要感谢;-)

在我写这篇文章的时候,我认为标记为continu作为可读的解决方案。

我问了一个关于Java中标签约定的问题(是否带有大写字母)。

基本上每个答案都告诉我"不要使用它们-总是有更好的方法!重构!"。因此,我发布了这个问题,以寻求一种更具可读性(因此更好的解决方案)。

到目前为止,到目前为止,我还没有完全相信替代方案。

请不要误会我的意思。标签在大多数时候都是邪恶的。

但就我而言,条件测试非常简单,该算法取自数学论文,因此很可能在不久的将来不会改变。因此,我宁愿一次看到所有相关部分,而不必滚动到另一个名为checkMatrixAtRow(x)的方法。

尤其是在更复杂的数学算法中,我发现很难找到"好的"函数名-但是我想这是另一个问题


Some come with a performance penalty of running the same test twice, which may not always be trivial. The alternative to that is storing and passing round booleans, which gets ugly.

性能损失很小。但是我同意两次运行测试不是一个好的解决方案。

我相信问题在于如何删除标签,而不是如何优化算法。在我看来,原始发布者没有意识到如何使用没有标签的'continue'和'break'关键字,但是,当然,我的假设可能是错误的。

在性能方面,该帖子未提供有关其他功能实现的任何信息,因此,据我所知,他们最好还是通过FTP下载结果,该结果由编译器内联的简单计算组成。

话虽如此,两次进行相同的测试在理论上并不是最佳的。

编辑:再三考虑,该示例实际上并不是对标签的可怕使用。我同意" goto是no-no",但不是因为这样的代码。此处使用标签实际上并不会以很大的方式影响代码的可读性。当然,它们不是必需的,可以轻易省略,但不能仅仅因为"使用标签不好"就不使用它们,在这种情况下,这不是一个好参数。毕竟,像其他人已经提到的那样,删除标签并不能使代码更容易阅读。


@尼古拉斯

Some of them are broken, or were before they were edited. Most damning is the fact that
people are having to think quite hard about how to write the code without labels and not
break anything.

我有不同的观点:其中一些因为难以弄清而破裂了
原始算法的行为。

我意识到这是主观的,但是阅读原始算法没有任何麻烦。它比拟议的替代产品更短,更清晰。

该线程中所有重构的作用是使用其他语言功能来模拟标签的行为-就像您将代码移植到没有标签的语言一样。


通过阅读您的代码。

  • 我注意到您消除了conditionAtVectorPosition处的无效矢量位置,然后删除了anotherConditionAtVector处的无效行。
  • 似乎检查anotherConditionAtVector上的行是多余的,因为无论idx的值是什么,anotherConditionAtVector仅取决于行索引(假设anotherConditionAtVector没有副作用)。

因此,您可以执行以下操作:

  • 首先使用conditionAtVectorPosition(这些是有效列)获取有效位置。
  • 然后使用anotherConditionAtVector获取有效行。
  • 最后,使用有效列和行使用conditionAtMatrixRowCol。

我希望这有帮助。


@Patrick您假设调用setValueInVector(v,idx);在第二个循环的末尾就可以了。如果代码在逻辑上是相同的,则必须将其重写为如下形式:

1
for( int idx = 0; idx

轻松,我的好人。

1
2
3
4
5
6
7
8
9
10
for( int idx = 0; idx < vectorLength; idx++) {
  if( conditionAtVectorPosition( v, idx ) ) continue;

  for( rowIdx = 0; rowIdx < n; rowIdx++ ) {
    if( anotherConditionAtVector( v, rowIdx ) ) continue;
    if( conditionAtMatrixRowCol( m, rowIdx, idx ) ) break;
  }
  if( !conditionAtMatrixRowCol( m, rowIdx, idx ) )
    setValueInVector( v, idx );
}

编辑:完全正确,你是安德斯。我还对解决方案进行了编辑,以考虑到这一点。


@Sadie:

They all look less readable than the original, in that they involve spending more code on the mechanism of the code rather than on the algorithm itself

将第二个循环外部化到算法外部不一定可读性较低。如果方法名称选择正确,则可以提高可读性。

Some of them are broken, or were before they were edited. Most damning is the fact that people are having to think quite hard about how to write the code without labels and not break anything.

我有不同的观点:其中一些被破坏是因为很难弄清楚原始算法的行为。

Some come with a performance penalty of running the same test twice, which may not always be trivial. The alternative to that is storing and passing round booleans, which gets ugly.

性能损失很小。但是我同意两次运行测试不是一个好的解决方案。

Refactoring the relevant part of the code into a method is effectively a no-op: it rearranges how the code is laid out in the file, but has no effect on how it's executed.

我不明白这一点。是的,它不会改变行为,例如...重构?

Certainly there are cases where labels are used incorrectly and should be refactored away. I just don't think it should be treated as some unbreakable rule.

我完全同意。但是正如您所指出的那样,我们中有些人在重构此示例时遇到了困难。即使最初的示例可读,也很难维护。


我不太确定首先要了解什么。
我会复制Gishu并写类似(如果有一些错误,抱歉):

1
2
3
4
5
6
7
8
9
10
11
for( int idx = 0; idx < vectorLength; idx++) {
    if( !conditionAtVectorPosition( v, idx ) && CheckedEntireMatrix(v))
        setValueInVector( v, idx );
}

inline bool CheckedEntireMatrix(Vector v) {
    for(rowIdx = 0; rowIdx < n; rowIdx++)
        if ( !anotherConditionAtVector(v,rowIdx) && conditionAtMatrixRowCol(m,rowIdx,idx) )
            return false;
    return true;
}

Gishu有一个正确的主意:

1
2
3
4
5
6
7
8
9
10
11
12
13
for( int idx = 0; idx < vectorLength; idx++) {
    if (!conditionAtVectorPosition( v, idx )
        && checkedRow(v, idx))
         setValueInVector( v, idx );
}

private boolean checkedRow(Vector v, int idx) {
    for( rowIdx = 0; rowIdx < n; rowIdx++ ) {
        if( anotherConditionAtVector( v, rowIdx ) ) continue;
        if( conditionAtMatrixRowCol( m, rowIdx, idx ) ) return false;
    }  
    return true;
}


这对您有用吗?我将内部循环提取到CheckedEntireMatrix方法中(您可以命名比我更好)-另外,我的Java有点生锈。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for( int idx = 0; idx < vectorLength; idx++) {
    if( conditionAtVectorPosition( v, idx )
    || !CheckedEntireMatrix(v)) continue;

    setValueInVector( v, idx );
}

private bool CheckedEntireMatrix(Vector v)
{
    for( rowIdx = 0; rowIdx < n; rowIdx++ ) {
        if( anotherConditionAtVector( v, rowIdx ) ) continue;
        if( conditionAtMatrixRowCol( m, rowIdx, idx ) ) return false;
    }  
    return true;
}


推荐阅读