代码之家  ›  专栏  ›  技术社区  ›  Mo.

重构标记的循环

  •  18
  • Mo.  · 技术社区  · 16 年前

    在我确信贴有标签的休息/继续是完全“不”后 here ,我需要帮助从代码中删除标签。

    我有一个正方形矩阵和一个长度相同的向量。矢量中已经有了一些值,这取决于矩阵中的值,矢量在循环中会发生变化。

    希望代码片段基本上可以理解

    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 );
    }     
    

    请让我相信,有一个更可读/更好的版本没有标签。

    12 回复  |  直到 11 年前
        1
  •  34
  •   Marcus Downing    16 年前

    看看迄今为止提出的解决方案:

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

    • 其中一些已损坏,或在编辑之前。最可恶的是,人们必须认真考虑如何编写没有标签的代码,而不是破坏任何东西。

    • 有些测试会有两次运行同一个测试的性能损失,这可能并不总是微不足道的。另一种选择是存储和传递布尔值,这会变得丑陋。

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

    所有这些使我相信,至少在这个问题的措辞,标签是正确的解决方案,不需要重构离开。当然,有些情况下标签使用不正确,应该重构掉。我只是不认为应该把它当作牢不可破的规则来对待。

        2
  •  1
  •   Anders Sandvig    16 年前

    @patrick您假设调用setvalueinvector(v,idx);在第二个循环的末尾是OK。如果代码在逻辑上是相同的,则必须将其重写为以下内容:

    for( int idx = 0; idx 
        3
  •  1
  •   Patrick    16 年前

    很容易,我的好人。

    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 );
    }
    

    编辑:很正确,你是安德斯。我已经编辑了我的解决方案,也考虑到了这一点。

        4
  •  1
  •   jfs    16 年前

    从阅读你的代码。

    • 我注意到您在ConditionAtvectorPosition中消除了无效的向量位置,然后在另一个ConditionAtvector中删除了无效的行。
    • 似乎在另一个ConditionAtVector处检查行是多余的,因为无论IDX的值是什么,另一个ConditionAtVector只依赖于行索引(假设另一个ConditionAtVector没有副作用)。

    所以你可以这样做:

    • 首先使用ConditionAtvectorPosition获取有效位置(这些是有效列)。
    • 然后使用其他条件atvector获取有效行。
    • 最后,使用ConditionAtMatrixRowcol并使用有效的列和行。

    我希望这有帮助。

        5
  •  1
  •   Community CDub    8 年前

    @ Nicolas

    其中一些已损坏,或在编辑之前。最可恶的是 人们不得不非常努力地思考如何编写没有标签的代码,而不是 打破任何东西。

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

    我知道这是主观的,但我在阅读原始算法时没有任何问题。它比提议的替代品更短更清晰。

    这个线程中的所有重构操作都是使用其他语言特性模拟标签的行为——就好像将代码移植到没有标签的语言中一样。

        6
  •  1
  •   Anders Sandvig    16 年前
    有些测试会有两次运行同一个测试的性能损失,这可能并不总是微不足道的。另一种选择是存储和传递布尔值,这会变得丑陋。
    性能罚款很小。然而,我同意两次测试不是一个好的解决方案。

    我认为问题是如何删除标签,而不是如何优化算法。在我看来,最初的海报不知道如何使用“继续”和“打破”没有标签的关键字,但当然,我的假设可能是错误的。

    在性能方面,post没有提供任何关于其他函数实现的信息,因此,据我所知,它们也可能通过ftp下载结果,因为它由编译器内联的简单计算组成。

    也就是说,两次做同样的测试在理论上并不是最佳的。

    编辑:再考虑一下,这个例子实际上并不是标签的可怕用法。我同意 "goto is a no-no" 但不是因为这样的代码。在这里使用标签实际上不会在很大程度上影响代码的可读性。当然,它们不是必需的,也很容易被省略,但在这种情况下,不能仅仅因为“使用标签是不好的”而使用它们。毕竟,删除标签并不能使代码更容易阅读,因为其他人已经发表了评论。

        7
  •  1
  •   Community CDub    8 年前

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

    在我写它的时候,我把标记为continue的解决方案看作是一个可读的解决方案。

    我问了这样一个问题。 question 关于JAVA中标签的约定(所有标签都有标签)。

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

    到目前为止,我还不完全相信目前提出的备选方案。

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

    但在我的例子中,条件测试非常简单,算法取自数学论文,因此很可能在不久的将来不会改变。所以我更喜欢让所有相关部分同时可见,而不是滚动到另一个名为checkmatrixatrow(x)的方法。

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

        8
  •  1
  •   RodeoClown    16 年前

    我认为贴有标签的循环是如此罕见,以至于你可以选择任何贴有标签的方法来为你工作——你所拥有的一切让你的意图继续清晰。


    在引导主管建议重构原始问题中的循环,现在看到有问题的代码之后,我认为这里有一个非常可读的循环。

    我所想象的是一段完全不同的代码——举一个实际的例子,我可以看到它比我想象的要干净得多。

    我为误会道歉。

        9
  •  0
  •   Gishu    16 年前

    这对你有用吗?我把内环提取成一个方法CheckedEntireMatrix(你可以把它命名得比我好)——我的Java也有点生锈了。但我认为它传达了信息

    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;
    }
    
        10
  •  0
  •   Nicolas    16 年前

    Gishu有正确的想法:

    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;
    }
    
        11
  •  0
  •   fulmicoton    16 年前

    我不太确定是否理解第一个继续。 我会抄写《吉斯书》,然后写一些类似的东西(如果有错误,我很抱歉):

    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;
    }
    
        12
  •  0
  •   Community CDub    8 年前

    @ Sadie :

    它们看起来都不如原来的可读性,因为它们需要在代码的机制上花费更多的代码,而不是在算法本身上。

    将第二个循环外部化到算法之外并不一定会降低可读性。如果方法名被正确选择,它可以提高可读性。

    其中一些已损坏,或在编辑之前。最可恶的是,人们必须认真考虑如何编写没有标签的代码,而不是破坏任何东西。

    我有一个不同的观点:其中一些是坏的,因为很难找出原始算法的行为。

    有些测试会有两次运行同一个测试的性能损失,这可能并不总是微不足道的。另一种选择是存储和传递布尔值,这会变得丑陋。

    性能罚款很小。然而,我同意两次测试不是一个好的解决方案。

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

    我不明白这一点。是的,它不会改变行为,就像…重构?

    当然,有些情况下标签使用不正确,应该重构掉。我只是不认为应该把它当作牢不可破的规则来对待。

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