2

我刚刚发现......再次......一个实时浪费错误如下

for (int i = 0; i < length; i++)
{ //...Lots of code 
    for (int j = 0; i < length; j++)
    {
        //...Lots of code 
    }
}

你有没有注意到应该是 j 的内部 i ?我也没有。所以从现在开始我将使用:

for (int i = 0; i < length; i++)
{
    for (int i1 = 0; i1 < length; i1++)
    {
    }
}

您对内部和外部 while 和 for 循环有什么建议?

编辑:感谢您的宝贵回复。特此简要总结建议的技巧:

  • 为索引变量使用有意义的变量名称(而不是我使用 SomeObjCollectionLength )
  • 将内循环的内容放入一个单独的方法中,并从外循环调用该方法
  • 外循环和内循环之间无法管理的代码行数是代码异味的强烈信号
  • 避免复制粘贴和匆忙,小心编写索引变量

您可能需要查看LBushkin的摘要以了解以下内容

  • 尽可能使用 foreach 和迭代器
  • 在进入循环之前初始化变量
  • 使每个循环只执行一个功能。避免在一个循环中混合职责
  • 如果可能的话,让你的循环足够短,以便一次查看所有内容
4

12 回答 12

7

不要使用 i & j(或任何其他单字母变量)作为索引名称。使用专有名称,您将不会遇到此类问题。

于 2009-06-18T09:33:18.140 回答
6

最简单和最干净的解决方案之一是将内部循环的内容放入一个方法中,使其变为:

for (int i = 0; i < length; i++)
{
    DoSomething();
}

private void DoSomething(int outerValue)
{
    for (int i = 0; i < length; i++)
    {
        // Do something else
    }

}
于 2009-06-18T09:32:45.910 回答
4

对我来说,这里的“代码味道”是“大量代码”。

如果循环中的代码量特别大,则内循环和外循环之间的距离意味着它们不太可能相互比较以确保正确性。

诚然,孤立地查看内部循环的开始应该会引起您的注意,但是将主要结构放在尽可能小的代码段中会使您的大脑难以消化。

可以将“大量代码”部分提取到单独的函数/方法中,以减小主要结构的大小——但这可能并不总是可行的。

另外,我想说“i1”不是一个特别好的变量名选择,因为这往往会鼓励“i2”、“i3”等,这并不能真正导致可理解的代码。也许用更有意义的东西替换所有循环变量将有助于代码的清晰性,并减少原始错误的机会。

于 2009-06-18T09:35:45.217 回答
4

我对编写更好的循环代码的最佳建议(没有特别的顺序)(其中大部分来自优秀的书Code Complete):

  1. 避免循环的多个出口点。
  2. 谨慎使用继续/中断。
  3. 尽可能将嵌套循环重构为单独的例程。
  4. 使用有意义的变量名使嵌套循环可读。
  5. 尽可能使用 foreach() 循环,而不是 for(i=...) 循环。
  6. 仅从一个位置输入循环。不要用 goto 跳入循环。曾经。
  7. 将初始化代码放在循环之前。
  8. 将循环初始化语句与它们相关的循环保持一致。
  9. 避免在非嵌套循环之间重用变量。10.将循环索引变量的范围限制为循环本身。
  10. 使用 while(true) 进行无限循环,而不是 for(;;)
  11. 在提供块结构的语言中(例如'{' 和'}')使用它们而不是缩进来包含循环语句。是的,即使是单线循环。
  12. 避免空循环。
  13. 避免将家务琐事放在循环的中间,而是将它们放在开头和/或结尾。
  14. 使每个循环只执行一个功能。避免在一个循环中混合职责。
  15. 使循环终止条件明显。
  16. 不要使用 for() 循环的循环索引变量来使其终止。
  17. 避免使用依赖于循环索引器最终值的代码。
  18. 考虑在复杂循环中使用安全计数器——可以检查它们以确保循环不会执行太多或太少的次数。
  19. 尽可能使用 break 语句来终止 while 循环。
  20. 如果可能的话,让你的循环足够短,以便一次查看所有内容。
于 2009-06-18T20:38:22.503 回答
3

这是一个复制粘贴错误,避免复制粘贴。

至于你的解决方案,它也好不到哪里去。错误仍然可以在大量代码之间溜走。即使是循环临时变量,我也倾向于使用有意义的名称。

于 2009-06-18T09:30:53.527 回答
2

在 VS 上利用您的 IDE,尝试使用它:http: //msdn.microsoft.com/en-us/library/z4c5cc9b (VS.80).aspx

示例:输入for ,然后依次按Tab Tab

于 2009-06-18T09:40:35.973 回答
2

我来这里是为了聪明并说“我只是第一次就写对了”。但是后来我看到了你的例子,而且,我自己已经做过太多次了。

当您需要这样的嵌套循环时,我唯一的解决方案是在编写代码时保持警惕和思考。

在可能的情况下,使用迭代器和 for each 循环很好。

另外,我看不出您建议的解决方案会变得更好。而且看起来也不怎么好看。

于 2009-06-18T09:42:21.990 回答
2

首先,减少循环体的大小,即将东西移动到单独的函数中。函数的长度超过屏幕可以容纳的长度通常是一个坏主意,因此循环应该更小。

其次,在这种情况下使用有意义的变量名。我只会在几行代码的简单循环中使用 i 和 j。例如,如果你正在处理一个二维数组,“col”和“row”会更有意义,让代码更容易阅读(“which was which?”)并且更容易发现这样的错误。

于 2009-06-18T09:47:52.620 回答
1

你只需要特别注意这些问题,没有灵丹妙药。即使您建议使用“更好的命名”,您有时也会忘记这是第 N 级还是第 (N+M) 级嵌套循环并出错。

如果需要嵌套循环,请仔细编写。如果可以通过将外部循环体提取到一个可以很好地防止索引滥用的函数来避免它。

于 2009-06-18T09:30:00.143 回答
0

如果我真的需要它们,我会使用 'ii' 和 'jj' 作为瞬态循环计数器 - 它们比 'i' 和 'j' 更容易搜索,并且在上述示例中也更容易发现。为了更好,您实际上可以使用真正的变量名。如果你正在循环一个字符串,那么你可以称它为 characterIndex 或其他东西。它需要更多的打字,但它会记录自己并节省以后调试晦涩问题的时间。

更好的是避免使用数字计数器并在集合上使用命名迭代器。在我看来,它们使意图更加清晰。

最后,如果可能的话,最好完全取消循环:Boost::Foreach是在 C++ 中执行此操作的一种方法,尽管我通常更喜欢使用 Python 等语言,它本机允许直接迭代容器的内容而无需需要增加索引值或迭代器。

于 2009-06-18T09:34:30.773 回答
0

尝试使用更多的声明性循环结构。例如,如果您真的不需要索引(那些is 和js)并且您的编程环境允许,您可以使用foreach构造来迭代集合。

于 2009-06-18T09:36:36.653 回答
0

就像在很多事情上一样,Steve McConnell 的Code Complete中有一些很好的建议。值得您花时间阅读他所说的关于构建良好循环代码的内容。我手头没有这本书的副本,但整本书值得你花时间。

于 2009-06-18T09:40:48.453 回答