循环中的最后一个元素是否值得单独处理?

发布于 2024-07-07 05:40:55 字数 977 浏览 11 评论 0原文

复习的时候,有时会遇到这样的循环:

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
}

现在我什至看到了 问题关于如何以良好的方式编写if子句......我很伤心:这里有些东西不对劲。

我错了吗? 如果是这样,那么在编码时用特殊情况来扰乱循环体有什么好处呢?

When reviewing, I sometimes encounter this kind of loop:

i = begin
while ( i != end ) {    
   // ... do stuff
   if ( i == end-1 (the one-but-last element) ) {
      ... do other stuff
   }
   increment i
}

Then I ask the question: would you write this?

i = begin
mid = ( end - begin ) / 2 // (the middle element)
while ( i != end ) {    
   // ... do stuff
   if ( i > mid ) {
      ... do other stuff
   }
   increment i
}

In my opinion, this beats the intention of writing a loop: you loop because there is something common to be done for each of the elements. Using this construct, for some of the elements you do something different. So, I conclude, you need a separate loop for those elements:

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
}

Now I even saw a question on SO on how to write the if-clause in a nice way... And I got sad: something isn't right here.

Am I wrong? If so, what's so good about cluttering the loop body with special cases, which you are aware of upfront, at coding time?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

倾城花音 2024-07-14 05:40:55

我认为这个问题不应该用一个原则来回答(例如“在循环中,平等地对待每个元素”)。 相反,您可以查看两个因素来评估实现的好坏:

  1. 运行时有效性 - 编译后的代码运行速度是否很快,或者以不同的方式运行会更快吗?
  2. 代码可维护性——(对于其他开发人员来说)是否容易理解这里发生的事情?

如果通过在一个循环中完成所有操作来更快并且代码更易读,那么就这样做。 如果速度较慢且可读性较差,请采用其他方式。

如果它更快但可读性较差,或者较慢但可读性更强,请找出在您的具体情况下哪个因素更重要,然后决定如何循环(或不循环)。

I don't think this question should be answered by a principle (e.g. "in a loop, treat every element equally"). Instead, you can look at two factors to evaluate if an implementation is good or bad:

  1. Runtime effectivity - does the compiled code run fast, or would it be faster doing it differently?
  2. Code maintainability - Is it easy (for another developer) to understand what is happening here?

If it is faster and the code is more readable by doing everything in one loop, do it that way. If it is slower and less readable, do it another way.

If it is faster and less readably, or slower but more readable, find out which of the factors matters more in your specific case, and then decide how to loop (or not to loop).

孤芳又自赏 2024-07-14 05:40:55

我知道当人们试图将数组的元素连接到逗号分隔的字符串中时,我已经看到了这一点:

for(i=0;i<elements.size;i++) {
   if (i>0) {
     string += ','
   }
   string += elements[i]
}

您要么在那里使用 if 子句,要么必须在末尾再次复制字符串 += 行。

在这种情况下,明显的解决方案是

string = elements.join(',')

但是 join 方法在内部执行相同的循环。 而且并不总是有一种方法可以做你想做的事。

I know I've seen this when people tried to join elements of an array into a comma-seperated string:

for(i=0;i<elements.size;i++) {
   if (i>0) {
     string += ','
   }
   string += elements[i]
}

You either have that if clause in there, or you have to duplicate the string += line again at the end.

The obvious solution in this case is

string = elements.join(',')

But the join method does the same loop internally. And there isn't always a method to do what you want.

梦中楼上月下 2024-07-14 05:40:55

在您发布的最后一个片段中,您正在重复 // .... 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 周期。 电话是你的。

In the last snippet you posted, you are repeating code for // .... do stuff.

It makes sense of keeping 2 loops when you have completely different set of operations on a different set of indices.

i = begin
mid = ( end - begin ) / 2 //(the middle element)
while ( i != mid ) {    
   // ... do stuff
   increment i
}

while ( i != end ) {
   // ... do other stuff
   increment i
}

This not being the case, you would still want to keep one single loop. However fact remains that you still save ( end - begin ) / 2 number of comparisons. So it boils down to whether you want your code to look neat or you want to save some CPU cycles. Call is yours.

浊酒尽余欢 2024-07-14 05:40:55

@xtofl,

我同意你的担忧。

我百万次遇到类似的问题。

开发人员为第一个或最后一个元素添加特殊处理。

在大多数情况下,值得从 startIdx + 1 或到 endIdx - 1 元素循环,甚至将一个长循环拆分为多个较短的循环。

在极少数情况下,不可能分割循环。

在我看来,不常见的事情应该尽可能在循环之外处理。

@xtofl,

I agree with your concern.

Million times I encountered similar problem.

Either developer adds special handling for first or for last element.

In most cases it is worth to just loop from startIdx + 1 or to endIdx - 1 element or even split one long loop into multiple shorter loops.

In a very rare cases it's not possible to split loop.

In my opinion uncommon things should be handled outside of the loop whenever possible.

云淡风轻 2024-07-14 05:40:55

我意识到,当我将特殊情况放入 for 循环中时,我通常太聪明了,不利于自己。

I came to a realization that when I put special cases in a for loop, I'm usually being too clever for my own good.

兮颜 2024-07-14 05:40:55

我想你已经完全明白了。 大多数人都会陷入在循环中包含条件分支的陷阱,而他们可以在外部执行这些操作:这更快

例如:

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循环应尽可能保持简单和简洁。

I think you have it entirely nailed. Most people fall into the trap of including conditional branches in loops, when they could do them outside: which is simply faster.

For example:

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

And the middle case you described is just plain nasty. Imagine if that code grows and needs to be refactored into different methods.

Unless you are parsing XML <grin> loops should be kept as simple and concise as possible.

把昨日还给我 2024-07-14 05:40:55

我认为你关于循环意味着平等地处理所有元素是正确的。 不幸的是,有时会出现特殊情况,这些情况应该通过 if 语句在循环构造内处理。

如果有很多特殊情况,您可能应该考虑想出某种方法来处理单独构造中的两组不同的元素。

I think you are right about the loop being meant to deal with all elements equally. Unfortunately sometimes there are special cases though and these should be dealt with inside the loop construct via if statements.

If there are lots of special cases though you should probably think about coming up with some way to deal with the two different sets of elements in separate constructs.

美男兮 2024-07-14 05:40:55

我更喜欢简单地从循环中排除该元素
并在循环外进行单独处理

例如:让我们考虑 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
}

I prefer to simply, exclude the element from the loop
and give a spearate treatment outside the loop

For eg: Lets consider the case of 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
}
难以启齿的温柔 2024-07-14 05:40:55

当然,将特殊外壳的东西放在一个可以拉出的循环中是愚蠢的。 不过,我也不会重复 do_stuff ; 我要么把它放在一个函数或一个宏中,这样我就不会复制粘贴代码。

Of course, special-casing things in a loop which can be pulled out is silly. I wouldn't duplicate the do_stuff either though; I'd either put it in a function or a macro so I don't copy-paste code.

唯憾梦倾城 2024-07-14 05:40:55

我讨厌看到的另一件事是 for-case 模式

for (i=0; i<5; i++)
{
  switch(i)
  {
    case 0:
      // something
      break;
    case 1:
      // something else
      break;
    // etc...
  }
}

我见过这个在真实的代码中。

Another thing I hate to see is the for-case pattern:

for (i=0; i<5; i++)
{
  switch(i)
  {
    case 0:
      // something
      break;
    case 1:
      // something else
      break;
    // etc...
  }
}

I've seen this in real code.

纵情客 2024-07-14 05:40:55

哪一个表现更好?

如果项目数量非常大,那么我总是会循环一次,特别是如果您要对每个项目执行一些操作。 评估条件的成本可能少于循环两次。

哎呀,你当然不会循环两次......在这种情况下,最好是两次循环。 但是,我认为首要考虑因素应该是性能。 如果您可以通过简单地操作循环边界(一次)来划分工作,则无需在循环中引入条件(N 次)。

Which one performs better?

If the number of items is very large then I would always loop once, especially if you are going to perform some operation on every item. The cost of evaluating the conditional is likely to be less than looping twice.

Oops, of course you are not looping twice... In which case two loops is preferable. However, I maintain that the primary consideration should be performance. There's no need to incur the conditional in the loop (N times) if you can partition the work by a simple manipulation of the loop bounds (once).

半夏半凉 2024-07-14 05:40:55

如果只执行一次,特殊情况应该在循环外完成。

但是,由于作用域的原因,可能有一个索引或一些其他变量更容易保留在循环内。 将数据结构上的所有操作保留在循环控制结构内也可能有上下文原因,尽管我认为这本身就是一个薄弱的论点。

The special case should be done outside the loop if it is only to be performed once.

However, there may be an index or some other variable(s) that are just easier to keep inside the loop due to scoping. There may also be a contextual reason for keeping all the operations on the datastructure together inside the loop control structure, though I think that is a weak argument on its own.

意中人 2024-07-14 05:40:55

只是根据需要和方便来使用它。 因此,没有提到平等地对待元素,并且将语言提供的功能结合起来当然也没有什么坏处。

Its just about using it as per need and convenience. There is as such no mentions to treat elements equally and there is certainly no harm clubbing the features which language provides.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文