循环中的最后一个元素是不是值得单独处理?
Posted
技术标签:
【中文标题】循环中的最后一个元素是不是值得单独处理?【英文标题】:Does the last element in a loop deserve a separate treatment?循环中的最后一个元素是否值得单独处理? 【发布时间】:2010-09-14 11:22:03 【问题描述】:在复习的时候,有时会遇到这样的循环:
i = begin
while ( i != end )
// ... do stuff
if ( i == end-1 (the one-but-last element) )
... do other stuff
increment i
那我问一个问题:你会写这个吗?
i = begin
mid = ( end - begin ) / 2 // (the middle element)
while ( i != end )
// ... do stuff
if ( i > mid )
... do other stuff
increment i
在我看来,这超出了编写循环的意图:你循环是因为每个元素都有一些共同的事情要做。使用此构造,您可以对某些元素执行不同的操作。因此,我得出结论,您需要为这些元素设置一个单独的循环:
i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid )
// ... do stuff
increment i
while ( i != end )
// ... do stuff
// ... do other stuff
increment i
现在我什至在 SO 上看到了一个 question,关于如何以一种很好的方式编写 if
-clause...我很伤心:这里有些不对劲。
我错了吗?如果是这样,那么在编码时将循环体与特殊情况混在一起有什么好处?
【问题讨论】:
这一切都很棒。我可以改善我的个人工作风格。谢谢 “你循环是因为每个元素都有一些共同的事情要做。”这取决于“某事”的普遍程度。所有元素都是汽车可能很常见。有些是蓝色的,有些是红色的。如果是蓝色,则选择蓝色,如果是红色,则选择红色进行绘制。因此,在某个时刻,循环中的决策可能是有意义的。但在某些时候,很明显你应该拆分循环。 @peter_the_oak:我同意您根据在循环中遇到的值做出决定,而不是根据索引。 【参考方案1】:当然,可以拉出的循环中的特殊外壳是愚蠢的。我也不会复制 do_stuff ;我要么把它放在一个函数或一个宏中,所以我不会复制粘贴代码。
【讨论】:
【参考方案2】:哪个表现更好?
如果项目的数量很大,那么我总是会循环一次,特别是如果你要对每个项目执行 some 操作。评估条件的成本可能低于循环两次。
糟糕,您当然不会循环两次……在这种情况下,最好循环两次。但是,我认为首要考虑因素应该是性能。如果您可以通过对循环边界的简单操作(一次)来划分工作,则无需在循环中产生条件(N次)。
【讨论】:
我从来没有提到循环两次 - 我暗示将循环分成两部分。【参考方案3】:我知道当人们试图将数组的元素连接成逗号分隔的字符串时,我已经看到了这种情况:
for(i=0;i<elements.size;i++)
if (i>0)
string += ','
string += elements[i]
你要么在那里有那个 if 子句,要么你必须在最后再次复制字符串 += 行。
这种情况下明显的解决方案是
string = elements.join(',')
但是 join 方法在内部执行相同的循环。而且并不总是有一种方法可以做你想做的事。
【讨论】:
【参考方案4】:我意识到当我将特殊情况放入 for 循环中时,我通常太聪明了。
【讨论】:
【参考方案5】:我认为您对循环旨在平等处理所有元素的看法是正确的。不幸的是,有时也有特殊情况,应该通过 if 语句在循环结构中处理这些情况。
如果有很多特殊情况,您可能应该考虑想出一些方法来处理不同构造中的两组不同元素。
【讨论】:
【参考方案6】:我讨厌看到的另一件事是for-case pattern:
for (i=0; i<5; i++)
switch(i)
case 0:
// something
break;
case 1:
// something else
break;
// etc...
我在真实的代码中见过这个。
【讨论】:
如果case只是按顺序执行,没有共同的代码,那就太傻了。但是,如果大多数迭代是由默认或通用代码处理的,并且有足够多的例外支持使用switch
而不是 if
测试,我认为使用 switch
处理分散的古怪案例没有任何问题(例如,有一个从 1 计数到 255 的循环,并且对 7、8、9、10、12、13 和 27 的索引值做一些奇怪的事情可能看起来很恶心,但有时程序必须对真实的东西进行建模世界)。【参考方案7】:
@xtofl,
我同意你的担心。
我遇到过类似的问题百万次。
开发人员为第一个或最后一个元素添加了特殊处理。
在大多数情况下,只需要从 startIdx + 1 或 endIdx - 1 元素循环,甚至将一个长循环拆分为多个较短的循环。
在极少数情况下,无法拆分循环。
在我看来不常见的事情应该尽可能在循环之外处理。
【讨论】:
【参考方案8】:我认为这个问题不应该由一个原则来回答(例如“在一个循环中,平等对待每个元素”)。相反,您可以查看两个因素来评估实施的好坏:
-
运行时有效性 - 编译后的代码运行速度是否快,还是以不同的方式运行会更快?
代码可维护性 - (对于其他开发人员)是否容易理解这里发生的事情?
如果通过在一个循环中完成所有事情来更快并且代码更具可读性,那么就这样做。如果速度较慢且可读性较差,请以另一种方式进行。
如果它更快但可读性更低,或者更慢但可读性更高,请找出在您的特定情况下哪些因素更重要,然后决定如何循环(或不循环)。
【讨论】:
【参考方案9】:在您发布的最后一个 sn-p 中,您正在重复代码 // .... do stuff.
当您对一组不同的索引进行完全不同的操作时,保持 2 个循环是有意义的。
i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid )
// ... do stuff
increment i
while ( i != end )
// ... do other stuff
increment i
情况并非如此,您仍然希望保留一个循环。然而事实仍然是你仍然保存 ( end - begin ) / 2 个比较。因此,归结为您是希望代码看起来整洁还是希望节省一些 CPU 周期。电话是你的。
【讨论】:
【参考方案10】:如果只执行一次,特殊情况应该在循环之外完成。
但是,由于作用域,可能有一个索引或其他一些变量更容易保留在循环中。将数据结构上的所有操作放在循环控制结构中可能还有一个上下文原因,尽管我认为这本身就是一个弱论点。
【讨论】:
【参考方案11】:它只是根据需要和方便使用它。因此,没有提到平等对待元素,而且将语言提供的功能组合在一起当然没有害处。
【讨论】:
【参考方案12】:我认为你已经完全搞定了。大多数人都陷入了在循环中包含条件分支的陷阱,而他们可以在外部执行它们:这只是更快。
例如:
if(items == null)
return null;
StringBuilder result = new StringBuilder();
if(items.Length != 0)
result.Append(items[0]); // Special case outside loop.
for(int i = 1; i < items.Length; i++) // Note: we start at element one.
result.Append(";");
result.Append(items[i]);
return result.ToString();
你描述的中间情况只是简单的讨厌。想象一下,如果该代码增长并需要重构为不同的方法。
除非您正在解析 XML,否则
【讨论】:
【参考方案13】:我更喜欢简单地从循环中排除元素 并在循环外进行单独处理
例如:让我们考虑 EOF 的情况
i = begin
while ( i != end -1 )
// ... do stuff for element from begn to second last element
increment i
if(given_array(end -1) != '')
// do stuff for the EOF element in the array
【讨论】:
以上是关于循环中的最后一个元素是不是值得单独处理?的主要内容,如果未能解决你的问题,请参考以下文章