【问题标题】:Most elegant looping construct?最优雅的循环结构?
【发布时间】:2010-09-22 14:46:31
【问题描述】:

很抱歉提出新问题。我还在学习编程。所以我使用的是 C++,我需要做这样的事情:

int n;
do {
    n = get_data();
    if(n != -1)
        send(n);
} while(n != -1);

这只是一个草图。反正感觉不是很优雅。我必须进行两次测试。我可以只测试一次并设置一个标志,但这也不是很优雅,因为我必须检查标志两次。似乎应该有一种方法可以更轻松地做到这一点,因为我想做的事情很容易。

【问题讨论】:

  • 老实说,我发现您的代码易于阅读。花时间缩短它(当你再次阅读它时,花更多时间在你的脑海中解开它)并不是你能做的最好的投资。谁在乎:它有效,高效且可读。转到下一行。

标签: c++ c loops


【解决方案1】:

break怎么样:

int n;
while(1) {
    n = get_data();
    if(n == -1)
        break;
    send(n);
}

这样你只测试一次,如果 get_data 没有返回你想要的,就立即退出。

【讨论】:

  • 我更喜欢这样,因为它最具表现力并且即使在 get_data() 变得更复杂时也可以扩展。在这种情况下,我还会在“永恒循环”中添加描述退出条件的注释,例如// 当 get_data() == -1 时中断。
  • 这是著名的“Loop and a Half”构造的一个实例:cs.duke.edu/~ola/patterns/plopd/loops.html#loop-and-a-half
  • 对我来说最明显也最优雅的。
【解决方案2】:
int n;
while (-1 != (n = get_data())) {
    send(n);
} // while

【讨论】:

  • 我鄙视人们把常数放在比较的左边。
  • 我的一些前同事对这种结构(条件表达式中嵌入的副作用)感到不快,一些教科书告诉你不要这样做。虽然我从来没有真正理解过为什么 - 我认为这很好,但有时出于政治原因最好避免。
  • 您将常量放在左侧,这样当您不小心将 == 或 != 更改为赋值运算符 (=) 时,就会出现编译器错误。在大多数现代语言中,这不再是问题,尤其是在现代 IDE 中。我仍然用 C++ 代码来做。
  • 将常量放在表达式的左侧确实可以更容易地避免意外错误,例如 if (n = -1)
  • 我很惊讶在如此短的循环结束时没有人评论'//while'。
【解决方案3】:

您的原始版本很好。

在我上一份 C 编程工作中,我们必须遵循 MISRA 编码标准。

根据 MISRA 规则,此:

int n;
while(1) {
    n = get_data();
    if(n == -1)
        break;
    send(n);
}

被禁止是因为break,而这个:

while((n = get_data()) != -1) { send(n); }

被禁止,因为赋值出现在布尔上下文中,所以我习惯于编写类似于原始版本的循环。

如果您认为布尔变量会使您的意图更清晰,或者如果测试是一个复杂的表达式,则可以使用布尔变量:

int n;
bool valid;
do {
    n = get_data();
    valid = n != -1;
    if(valid)
        send(n);
} while(valid);

但对于像“n != -1”这样的简单测试,可能不值得让程序更长。

【讨论】:

  • 我找不到该 MISRA 标准的在线版本(不过,您可以花 25 美元购买一本书......)。根据您的描述,感觉有点愚蠢,将笨重的编码风格强加给程序员。
  • @Harleqin,你是对的,生成的代码可能很庞大,有些规则是多余的,还有一些在嵌入式平台之外没有意义。
  • MISRA 强制没有代码是只写的。它的规则是让任何程序不言自明。当然,有些可能会让您提高性能,但众所周知,应该在您拥有功能程序之后进行优化。如果你得到一个清晰可读的代码,你就允许其他人重复使用它。
  • @jpinto3912 - 从这个例子看来,MISRA 有时可以强制执行相反的操作。
【解决方案4】:

类似于 eed3si9n,但可以说更容易阅读:

 int n;
 while (n = get_data(), n != -1)
 {
     send(n);
 }

【讨论】:

  • 我不得不反对更容易阅读。大多数程序员不理解 C 中的逗号运算符,并偶然发现了这种类型的结构。
  • 另一方面,许多程序员也很难接受 operator= 返回赋值的事实,而且它的使用很容易导致其他地方提到的错误。但这就是为什么我说“可以说”而不是“肯定”:-)
  • 即使您对逗号不够熟悉,这个版本看起来确实更干净。我觉得看到它但不习惯逗号的人仍然能够比 eed3si9n 的版本更快地解析它。
【解决方案5】:
for (int n = get_data(); n != -1; n = get_data()) {
  send(n);
}

【讨论】:

  • 如果要保存一行,请取出花括号:)
  • 是的,如果您确实想让它更短(我不想),这将是最好的方法。至少,巧妙地使用了 for 循环。
【解决方案6】:

使用goto 构造:)

int n;
goto inside; do {
  send(n);
inside:
  n=get_data();
} while(n!=-1);

【讨论】:

  • do 循环和 while 循环之间没有区别,如果你从不从顶部输入它,是吗?如果不是,我会使用更熟悉的“while”形式。
  • 是的,你是对的,没有真正的区别。这只是个人喜好问题 :) 我喜欢 do-while 形式,因为获取数据的行和对此数据进行测试的行(循环条件)一个接一个。
  • 我通常会投反对票,因为格式很难阅读,但这是一种有趣的方法。
【解决方案7】:
int get_data()
{
 ...
}

void _send(int )
{
 ...
}

int  send(int (*a) ())
{
   int n = a();

   if (n == -1)
      return n;

   _send(n);
   return 1;
}

int main()
{
  int (*fp)();
  fp = get_data;
  while ( send(fp)!= -1 );

  return 0;
}

HTH

【讨论】:

    【解决方案8】:
    int n;
    n = get_data();
    while (n != -1) {
      send(n);
      n = get_data();
    }
    

    【讨论】:

    • 这是被教导在 cobol 中执行循环的方式,早在很久以前。一个典型的编译器会倾向于以一种有效的形式编译它吗?根据我所见,lazydens 解决方案通常是编译器为了提高指令缓存效率而生成的解决方案
    【解决方案9】:

    在 Python 中你会这样做:

    while True:
        n = get_data()
        if n == -1:
            break
        send(n)
    

    如您所见,这并不比您的 C 版本短多少。 通常,Python 代码比其 C 等效代码小 5 到 10 倍。这不是这里的情况,因此您可以享受可读、足够短、快速的 sn-p,并专注于其他事情。

    如果您确实想让它更短,请查看 Jonathan 的答案,但这并不重要。

    【讨论】:

      【解决方案10】:
      /* This is cleaner */ 
      AGAIN:;
          int n = get_data();
          if (n != -1)
          {
              send(n);
              goto AGAIN;
          }
      
      
      /* This has some charm as well */ 
      int n;
      while ((n = get_data()) != -1)
          send(n);
      /* and now i see that this is the top answer.  Oh well */ 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-11-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多