【问题标题】:Useful alternative control structures?有用的替代控制结构?
【发布时间】:2011-05-16 16:15:02
【问题描述】:

有时当我在编程时,我发现某些特定的控制结构对我来说非常有用,但在我的编程语言中并没有直接可用。我认为我最常见的愿望是“分手”(我不知道实际上该怎么称呼它):

{
    foo();
} split_while( condition ) {
    bar();
}

这段代码的语义是foo() 总是运行,然后检查条件。如果为真,则运行bar(),然后我们返回第一个块(因此再次运行foo(),等等)。感谢a comment by reddit user zxqdms,我了解到Donald E. Knuth 在他的论文"Structured programming with go to statements"(参见第279 页)中写到了这个结构。

您认为哪些替代控制结构是组织计算的有用方式?

我的目标是为自己和其他人提供有关构建代码的新思维方式,以改进分块和推理。

注意:我不是在问如何概括所有可能的控制结构,无论是使用jneif/goto、Lisp 宏、延续、单子、组合子、夸克或其他任何东西。我在问什么专业化对描述代码有用。

【问题讨论】:

标签: loops programming-languages control-structure


【解决方案1】:

用一个移动的窗口(n 个元素而不是 1 个)遍历一个列表怎么样? 我认为这与@munificent 的answer 切线相关。

类似

#python
#sum of adjacent elements
for x,y in pairs(list):
    print x + y

def pairs(l):              
    i=0                    
    while i < len(l)-1:    
        yield (l[i],l[i+1])
        i+=1               

它对某些类型的东西很有用。不要误解我的意思,这很容易实现为一个函数,但我认为当有更具体/描述性的工具用于这项工作时,很多人会尝试带出 forwhile 循环。

【讨论】:

    【解决方案2】:
    foo();
    
    while(condition)
    {
       bar();
       foo();
    }
    

    【讨论】:

    • 这甚至没有做他的slit_while应该做的事情。真正的问题是您能想到哪些新的高级控制结构,而不是如何实施它们。
    • while (true) { foo(); if (!condition) break; Bar(); }
    【解决方案3】:

    在许多语言中不可用的控制结构之一是 case-in 类型结构。类似于 switch 类型结构,它允许您拥有一个格式整齐的可能选项列表,但匹配第一个正确的选项(而不是第一个匹配输入的选项)。此类的 LISP(确实有):

    (cond
       ((evenp a) a)        ;if a is even return a
       ((> a 7) (/ a 2))    ;else if a is bigger than 7 return a/2
       ((< a 5) (- a 1))    ;else if a is smaller than 5 return a-1
       (t 17))              ;else return 17
    

    或者,对于那些更喜欢 C 格式的人

    cond 
        (a % 2 == 0): 
            a;     break;
        (a > 7):
            a / 2; break;
        (a < 5):
            a - 1; break;
        default:
            17;    break;
    

    它基本上比 switch 更准确地表示 if/elseif/elseif/else 构造,并且它可以非常方便地以干净、易读的方式表达该逻辑。

    【讨论】:

      【解决方案4】:

      这与@Paul Keister的回复类似。

      (咕哝,咕哝)几年前,我正在处理的应用程序有很多所谓的控制中断处理的变体——所有这些逻辑都会将已排序的数据行分解为带有页眉和页脚的组和子组.由于应用程序是用 LISP 编写的,我们在一个名为 WITH-CONTROL-BREAKS 的宏中捕获了常见的习惯用法。如果我将该语法转换为流行的波浪形形式,它可能看起来像这样:

      withControlBreaks (x, y, z : readSortedRecords()) {
        first (x) :     { emitHeader(x); subcount = 0; }
        first (x, y) :  { emitSubheader(x, y); zTotal = 0; }
        all (x, y, z) : { emitDetail(x, y, z); ztotal += z; }
        last (x, y) :   { emitSubfooter(x, y, zTotal); ++subCount; }
        last (x) :      { emitFooter(x, subcount); }
      }
      

      在 SQL、XQuery、LINQ 等广泛使用的现代时代,这种需求似乎不像以前那样出现了。但有时,我希望我手头有这种控制结构。

      【讨论】:

        【解决方案5】:

        如果你看一下 Haskell,虽然各种控制结构都有特殊的语法,但控制流通常是由类型捕获的。最常见的此类控制类型是 Monads、Arrows 和 applicative functors。因此,如果你想要一种特殊类型的控制流,它通常是某种高阶函数,你可以自己编写或者在 Haskells 包数据库(Hackage)中找到一个,它非常大。

        此类函数通常位于 Control 命名空间中,您可以在其中找到用于并行执行和错误处理的模块。通常在过程语言中发现的许多控制结构在 Control.Monad 中都有对应的函数,其中包括循环和 if 语句。 if-else 是 haskell 中的关键字表达式,如果没有 else 在表达式中没有意义,但在 monad 中完全有意义,所以没有 else 的 if 语句被函数 whenunless 捕获。

        另一个常见的情况是在更一般的上下文中进行列表操作。函数式语言非常喜欢fold,以及像mapfilter 这样的Specialized 版本。如果你有一个 monad,那么fold 会自然延伸到它。这被称为foldM,因此还有你能想到的任何特定折叠版本的扩展,比如mapMfilterM

        【讨论】:

          【解决方案6】:

          怎么样

          alternate {
              statement 1,
              statement 2,
              [statement 3,...]
          }
          

          用于在每次连续传递时循环使用可用语句。

          编辑:简单的例子

          table_row_color = alternate(RED, GREEN, BLUE);
          
          player_color = alternate(color_list); // cycles through list items
          
          alternate(
              led_on(),
              led_off()
          );
          

          编辑 2:在上面的第三个示例中,语法可能有点令人困惑,因为它看起来像一个函数。事实上,每次传递只评估一个语句,而不是两个。更好的语法可能类似于

          alternate {
              led_on();
          }
          then {
              led_off();
          }
          

          或者类似的东西。不过,我确实喜欢这样的想法,即如果需要,可以使用调用的结果(如颜色示例中所示)。

          【讨论】:

          • 这很有趣。你能给我一个示例用例吗?
          • 这是一个怎样的控制结构?
          • @missingfaktor:因为每次通过“alternate()”后控制都会发生变化。如果实现为函数(在大多数语言中),则 led_on() 和 led_off() 将在调用 Alternate 之前被调用。它可以用 lambdas 和一个非局部变量来模拟以保持状态,但这是额外的复杂性。另一方面,这似乎是一种罕见的需求,不值得拥有特定的语法。毕竟,在 Python 中使用 itertools.cycle() 很容易实现类似的东西。
          • +1 的想法。我认为这可能会按照您的建议...关于生成器和协程!编辑:格式糟糕
          • 您可以只使用带有闭包的函数来完成这种事情。例如,在 JavaScript 中,您可以编写以下代码: function cycle(choices) { var i = 0;返回函数() { var current = i; i = (i + 1) % 选择。长度;返回选择[当前]; };你可以像这样使用这个代码: var colors = cycle(["blue", "white", "red"]); for (var i = 0; i
          【解决方案7】:

          我想我应该提一下 CityScriptCityDesk 的脚本语言),它有一些非常奇特的循环结构。

          来自帮助文件:

          {$ forEach n var in (condition) sort-order $}
          ... text which appears for each item ....
          {$ between $}
          .. text which appears between each two items ....
          {$ odd $}
          .. text which appears for every other item, including the first ....
          {$ even $}
          .. text which appears for every other item, starting with the second ....
          {$ else $}
          .. text which appears if there are no items matching condition ....
          {$ before $}
          ..text which appears before the loop, only if there are items matching condition
          {$ after $}
          ..text which appears after the loop, only of there are items matching condition
          {$ next $}
          

          【讨论】:

            【解决方案8】:

            替代品

            bool found = false;
            for (int i = 0; i < N; i++) {
              if (hasProperty(A[i])) {
                found = true;
                DoSomething(A[i]);
                break;
              }
            }
            if (!found) {
              ...
            }
            

            喜欢

            for (int i = 0; i < N; i++) {
              if (hasProperty(A[i])) {
                DoSomething(A[i]);
                break;
              }
            } ifnotinterrupted {
              ...
            }
            

            我总觉得必须有更好的方法,而不是在循环体的最后一次(常规)执行之后引入一个标志来执行某些事情。可以检查!(i &lt; N),但i 在循环后超出范围。

            【讨论】:

            • 我真的很喜欢“ifnotinterrupted”之类的东西。
            • Python 有这个:forwhile 有一个(可选的)else 块,只有当它们“自然”完成时才会运行(即没有break)。
            • @chpwn:哇,似乎我在 Python 中循环后误解了else(我从未使用过);谢谢你告诉我。但是,我认为“else”用词不当。
            【解决方案9】:

            我想查看用于分组输出的关键字。而不是这个:

                    int lastValue = 0;
            
                    foreach (var val in dataSource)
                    {
                        if (lastValue != val.CustomerID)
                        {                    
                            WriteFooter(lastValue);
                            WriteHeader(val);
                            lastValue = val.CustomerID;
                        }
                        WriteRow(val);
                    }
                    if (lastValue != 0)
                    {
                        WriteFooter(lastValue);
                    }
            

            这样的事情怎么样:

                    foreach(var val in dataSource)
                    groupon(val.CustomerID)
                    {            
                        startgroup
                        {
                            WriteHeader(val);
                        }
                        endgroup
                        {
                            WriteFooter(val)
                        }
                    }
                    each
                    {
                        WriteRow(val);
                    }
            

            如果您有不错的平台、控件和/或报告格式,则无需编写此代码。但令人惊讶的是,我发现自己经常这样做。最烦人的部分是最后一次迭代之后的页脚——在不重复代码的情况下,在现实生活中很难做到这一点。

            【讨论】:

            • +1 前段时间在 VB6 中工作时我错过了这个。另外,ABAP has this.
            • 很有意思,这就是我想到的专业。整个迭代的 At First 和 At Last 是一个很好的接触。
            • 在 Harbor 中,如果您位于第一个和最后一个项目,您可以使用 item:__enumIsFirst 和 item:__enumIsLast 返回一个布尔值
            【解决方案10】:
            for int i := 0 [down]to UpperBound() [step 2]
            

            在所有 C 派生语言中都缺少。

            请在投票或发表评论之前考虑一下
            这对for (int i = 0; i &lt;= UpperBound(); i++) 来说并不多余,它具有不同的语义:

            1. UpperBound() 只计算一次

            2. UpperBound() == MAX_INT 的情况不会产生无限循环

            【讨论】:

              【解决方案11】:

              PL/I 风格的“for”循环范围怎么样? VB 等价物是:

              ' 计数 1, 2, ... 49, 50, 23, 999, 998, ..., 991, 990 对于 I = 1 到 50、23、999 到 990 步长 -1

              我能看到的最常见的用法是循环运行一个索引列表,然后再输入一个。顺便说一句,For-Each 的用法也很方便:

              ' Bar1, Bar2, Bar3 是一个 IEnum(Wazoo);博兹是个Wazoo 对于 Bar1、Bar2、Enumerable.One(Boz)、Bar3 中的每个 Foo 作为 Wazoo

              这将对 Bar1 中的所有项目、Bar2、Boz 和 Bar3 中的所有项目运行循环。 Linq 可能会毫不费力地允许这样做,但内在的语言支持可能会更有效。

              【讨论】:

                【解决方案12】:

                大多数语言都有内置函数来涵盖常见情况,但“fencepost”循环总是一件苦差事:循环中您想在每次迭代中做某事并在次迭代之间做其他事情.例如,用分隔符连接字符串:

                string result = "";
                for (int i = 0; i < items.Count; i++) {
                    result += items[i];
                    if (i < items.Count - 1) result += ", "; // This is gross.
                    // What if I can't access items by index?
                    // I have off-by-one errors *every* time I do this.
                }
                

                我知道折叠可以覆盖这种情况,但有时您需要一些必要的东西。如果你能做到,那就太酷了:

                string result = "";
                foreach (var item in items) {
                    result += item;
                } between {
                    result += ", ";
                }
                

                【讨论】:

                • 我喜欢它,但在 'between' 块期间,'item' 是否在范围内,如果是,它是上一个还是下一个?如果我想将 ["a", "1", "b", "2"] 之类的东西变成 "a=1,b=2",那么我需要在 "=" 和 "," 之间交替。如何在 between 块中获得循环索引?还是我需要一个非局部变量?
                • 那太好了。 split_while 解决了这个问题,但仅适用于 while 而不是其他常见循环。
                • @Andrew,您始终可以同时提供循环中的上一项和下一项:between(prev, next) {
                • 在 Haskell 中使用“intersperse”,这不会是一件苦差事,恕我直言。
                • @Andrew,这就是重点。 between 块只会在两个项目之间执行,所以不会在最后一个之后执行。
                【解决方案13】:
                if (cond)
                   //do something
                else (cond)
                   //do something
                else (cond)
                   //do something
                first
                   //do something
                then
                   //do something
                else (cond)
                   //do something
                else
                   //do something
                end
                

                如果 3 个条件中的任何一个被评估为真,则 FIRST 和 THEN 块运行。 FIRST 块在条件块之前运行,THEN 在条件块运行之后运行。

                在 FIRST 和 THEN 语句之后的 ELSE 条件或最终写入独立于这些块。

                它可以读作:

                if (cond)
                   first()
                   //do something
                   then()
                else (cond)
                   first()
                   //do something
                   then()
                else (cond)
                   first()
                   //do something
                   then()
                else (cond)
                   //do something
                else
                   //do something
                end
                
                
                function first()
                   //do something
                return
                function then()
                   //do something
                return
                

                这些函数只是一种阅读形式。他们不会创造范围。它更像是来自 Basic 的 gosub/return。

                作为讨论事项的有用性和可读性。

                【讨论】:

                  【解决方案14】:

                  这只是一个一般的想法和语法:

                  if (cond)
                     //do something
                  else (cond)
                     //do something
                  also (cond)
                     //do something
                  else
                     //do something
                  end
                  

                  ALSO 条件总是被评估。 ELSE 照常工作。

                  它也适用于案例。大概是消除break语句的好办法:

                  case (exp)
                     also (const)
                        //do something
                     else (const)
                        //do something
                     also (const)
                        //do something
                     else
                        //do something
                  end
                  

                  可以读作:

                  switch (exp)
                     case (const)
                        //do something
                     case (const)
                        //do something
                        break
                     case (const)
                        //do something
                     default
                        //do something
                  end
                  

                  我不知道这是否有用或易于阅读,但这是一个示例。

                  【讨论】:

                    【解决方案15】:

                    我建议使用“then”运算符。它在第一次迭代时返回左操作数,在所有其他迭代中返回右操作数:

                    var result = "";
                    foreach (var item in items) {
                        result += "" then ", ";
                        result += item;
                    }
                    

                    在第一次迭代中,它在结果中添加“”,在所有其他迭代中添加“,”,因此您会得到一个字符串,其中包含以逗号分隔的每个项目。

                    【讨论】:

                    • 如果在 for 循环内的任何地方都允许有一个新块...也许有一个 else...first { ... } else { result += ", "; }
                    【解决方案16】:

                    D 的scope guards 是一种非常有用的控制结构,但并不常见。

                    【讨论】:

                    • 它们类似于 Go 语言中的“defer”。
                    【解决方案17】:

                    标记循环是我发现自己有时在主流语言中缺少的东西。例如,

                    int i, j;
                    for outer ( i = 0; i < M; ++i )
                        for ( j = 0; j < N; ++j )
                            if ( l1[ i ] == l2[ j ] )
                               break outer;
                    

                    是的,我通常可以用goto 来模拟这个,但是continue 的等价物需要你将增量移动到标签之后的循环体末尾,这会损害可读性。您也可以通过在内循环中设置一个标志并在外循环的每次迭代中检查它来做到这一点,但它总是看起来很笨拙。

                    (奖励:我有时希望redocontinuebreak 一起使用。它会返回到循环的开头而不评估增量。)

                    【讨论】:

                    • 您还可以模拟多级中断并继续异常。它不是最优雅的解决方案。我确实不得不求助一次。从好的方面来说,即使最里面的循环已经被提取到另一个函数中,它也可以工作。
                    • 郑重声明,JavaScript 和 Java 都有这个。
                    • +1 表示重做。我发现自己想要这个,尤其是在列表中只遍历和插入/擦除几次时。 (在c++中就是不知道在other中能不能做好)
                    【解决方案18】:

                    这可能不算数,但在 Python 中,我很不高兴没有 do 循环。

                    为了确保我的答案没有得到任何支持,我最终会对我在任何时间使用的任何语言都没有 goto 感到恼火。

                    【讨论】:

                    • 有一篇论文证明异常可以比延续更强大。延续在表达能力上基本上等同于GOTO。所以,如果你的语言有例外,它有GOTO
                    • @Jörg:你能把论文的链接贴出来吗?
                    • @missingfaktor:论文的标题是[Unchecked Exceptions can be Strictly More Powerful than Call/CC](Lambda-the-Ultimate.Org/node/2966)。请注意,与一篇科学论文一样,人们必须非常仔细地阅读什么它实际上论文证明的。在这种特殊情况下,他们表明将 SML 样式的异常添加到简单类型的 λ 演算(已知不是图灵完备的)使其成为图灵完备的,而将 call/cc 添加到系统 Fω(已知是 STλC 的严格超集)不是。 (使其 TC 打破类型健全性的唯一方法。)
                    • 这不仅仅是学术诡计,顺便说一句。我知道至少有一个编译器使用 ECMAScript 异常来实现其他控制流结构:Microsoft Live Labs 的 Volta 项目(除其他外)是一个将任意 CIL 字节码编译为 ECMAScript 源代码的编译器。 CIL(以及更普遍的 .NET 和 C#)具有 ECMAScript 所没有的各种控制流:GOTO、协程、线程(!),所有这些都是使用 ECMAScript 异常实现的。
                    • 还要注意:“可以比”更强大并不一定意味着“可以被人类使用”。特别是,我相信为了使用像 GOTO 这样的异常,您必须以持续传递样式重写代码,然后将您的持续传递样式编码为异常。
                    【解决方案19】:

                    如果您主要使用非函数式语言,那么 Python 中的生成器确实很新颖。更一般地说:延续、协同例程、惰性列表。

                    【讨论】:

                      【解决方案20】:

                      用 else 循环:

                      while (condition) {
                        // ...
                      }
                      else {
                        // the else runs if the loop didn't run
                      }
                      

                      【讨论】:

                      • 如果循环运行,foo 不会在第一次运行时运行,只会在最后运行。这与他所描述的相反。
                      • Python 正是这种结构。此外,它允许将 'else:' 添加到 'for:' 和 'try:'。
                      • 在 Python 中,如果循环“正常”终止,则 else 将运行,它要么不间断地执行,要么从未运行。
                      • +1,但对我来说,主要是希望 for(){...} else {...}
                      • @tstenner:当我用 else 说“循环”时,我把它包括在我的问题中。只是我提供了一个while循环示例。但是,是的,我们的想法是让任何循环类型都带有 else。
                      【解决方案21】:

                      ignoring - 忽略特定代码块中发生的异常。

                      try {
                        foo()
                      } catch {
                        case ex: SomeException => /* ignore */
                        case ex: SomeOtherException => /* ignore */
                      }
                      

                      使用ignoring 控件结构,您可以更简洁、更易读地编写如下:

                      ignoring(classOf[SomeException], classOf[SomeOtherException]) {
                        foo()
                      }
                      

                      [ Scala 在其标准库中的util.control 包中提供了这个(以及许多其他异常处理控制结构)。 ]

                      【讨论】:

                      • 忽略异常通常类似于吞下一个错误。这对大多数应用程序都不利。
                      • @Jordão: There are cases where you might need to do that 在这种情况下,这种控制结构确实让代码更简洁。
                      • 至少,软件应该注意它忽略了某个异常。向异常类添加一些指示它可以被忽略的指令也是一个好主意,以防止滥用此功能。
                      【解决方案22】:

                      如果没有:

                      unless (condition) {
                        // ...
                      }
                      

                      虽然不是:

                      until (condition) {
                        // ...
                      }
                      

                      【讨论】:

                      • 我花了一段时间来适应它们(在 Perl 中),但我确实觉得我现在在本地使用它们。
                      • Scala:def unless(cond: =&gt; Boolean)(f: =&gt; Unit) { if(!cond) f }def until(cond: =&gt; Boolean)(f: =&gt; Unit) { while(!cond) f }。 :D
                      • 我实际上不喜欢 Perl 中的它们。我尝试使用它们,但发现它们混淆了代码而不是使其更清晰。也许是因为在其他语言中通常不使用这样的结构......
                      • 澄清我想说的是:在其他语言中,通常使用 if (!..)while (!..) 代替,这使得它们在跨语言中更具惯用性。
                      • 我认为这些结构可以使 intent 更清晰:比较,例如,while(!done)until(done)
                      【解决方案23】:

                      有时,我需要一个带有索引的 foreach 循环。可以这样写:

                      foreach (index i) (var item in list) {
                        // ...
                      }
                      

                      (我不是特别喜欢这种语法,但你明白了)

                      【讨论】:

                      • 在 python 中,这是使用内置的 enumerate 函数处理的,该函数接受一个列表并返回 (index, item) 对的列表。 “对于我,枚举(列表)中的项目”。
                      • 绝对!我经常写一个foreach 循环,但后来意识到我需要一个索引。
                      • 在 Ruby 中我们使用 Array#each_with_index
                      • 在 Harbor 中,您有 item:__enumIndex 来检索它。此外,您可以使用返回 bool 的 item:__enumIsFirstitem:__enumIsLast。值得注意的是,Harbor 还允许降序 foreach 和同时使用多个数组的 foreach。降序对于在循环期间删除项目而不会出现同步问题非常有用。
                      【解决方案24】:

                      一个相当常见的是无限循环。我想这样写:

                      forever {
                        // ...
                      }
                      

                      【讨论】:

                      • #define ever (;;) for ever {}
                      • @Jookia: #define forever for(;;)
                      • @Jordão:Scala 没有这两种控制结构——breakcontinue。它主要是一种函数式语言,尽管可用,但通常不鼓励使用命令式功能。由于大众的需求,他们最近提供了break(虽然是以库方法的形式)。我们仍然没有continue(我认为我们永远不会有,因为您几乎不需要用函数式语言编写您的手动循环。)
                      • forever 肯定比while(true) 漂亮很多!
                      • @Jordão:我不特别喜欢“一次”这个词,但我不确定哪个更好。对于像 VB 这样的单词配对语言,也许只是“块”,所以它可以与“结束块”配对。对于 C 派生词,如果不需要跳转到结束语义,则可以使用不带任何关键字的大括号,尽管最好有一些标记表明大括号对存在用于范围界定(而不是分隔“如果" 不小心被删除了)。
                      【解决方案25】:

                      这有点开玩笑,但你可以像这样得到你想要的行为:

                      #include <iostream>
                      #include <cstdlib>
                      
                      int main (int argc, char *argv[])
                      {
                        int N = std::strtol(argv[1], 0, 10); // Danger!
                        int state = 0;
                        switch (state%2) // Similar to Duff's device.
                        {
                          do {
                            case 1: std::cout << (2*state) << " B" << std::endl;
                            case 0: std::cout << (2*state+1) << " A" << std::endl; ++state;
                          } while (state <= N);
                            default: break;
                        }
                      
                        return 0;
                      }
                      

                      附言格式化有点困难,我绝对不满意;然而,emacs 做得更糟。有人想试试 vim 吗?

                      【讨论】:

                      • 这不是 OP 问题的答案。
                      【解决方案26】:

                      还要注意,许多控制结构在 monadic 上下文中获得了新的含义,具体取决于特定的 monad - 查看 Haskell 中的 mapM、filterM、whileM、sequence 等。

                      【讨论】:

                        【解决方案27】:

                        使用(lisp 风格的)宏、尾调用和延续,所有这些都很古怪。

                        使用宏,如果标准控制流构造不足以满足给定的应用程序,程序员可以编写自己的(以及更多)。它只需要一个简单的宏来实现您作为示例提供的构造。

                        使用尾调用,可以将复杂的控制流模式(例如实现状态机)分解为函数。

                        Continuations 是一种强大的控制流原语(try/catch 是它们的受限版本)。结合尾调用和宏,复杂的控制流模式(回溯、解析等)变得直截了当。此外,它们在 Web 编程中很有用,因为使用它们可以反转控制反转;你可以有一个函数来询问用户一些输入,做一些处理,要求用户提供更多输入,等等。

                        套用 Scheme 标准,而不是在您的语言中堆积更多功能,您应该设法消除使其他功能显得必要的限制。

                        【讨论】:

                        • 我实际上很确定宏 + 尾调用 + 定界延续(可能 + 可变状态)确实 所有可能的控制结构的通用基础。我这么认为是因为 Cont monad 是万物之母 :)
                        • 这仍然没有解决您将实现什么控制结构的问题。好的,Lisp 宏概括了我需要的所有控制结构;好吧太好了,也许它们不应该是核心语言。这仍然不能回答我选择的哪些专业。我们在历史上发现ifwhile 可能很有用——还有什么用处?
                        • @A. Rex:实际上,我不记得我上次在某种非常特殊上下文之外使用while 循环是什么时候了。我相信最后一次是在一个手写的递归下降解析器中,我将它更多地用作 DSL 元素而不是实际的循环。我通常使用地图、折叠、变形、过滤器、变压器。我什至不经常使用普通的迭代器。在现代语言中,几乎唯一 使用while 循环的原因是当您想要一个无限 循环时,例如在操作系统的事件循环中。但为此,您不妨使用尾递归函数。
                        • 吹毛求疵:有一篇论文证明了异常可以比延续更强大。 (事实上​​,对异常的常见批评之一是它们只是 GOTO 的不同名称。)因此,从技术上讲,延续是 try/catch 的受限版本。但这是最糟糕的一种:-)我完全理解你的意思。但它可能很有用:例如,Volta CIL->ECMAScript 编译器使用异常来实现延续和线程。
                        【解决方案28】:
                        {
                            foo();
                        } split_while( condition ) {
                            bar();
                        }
                        

                        您可以使用普通的while 轻松完成此操作:

                        while (true) {
                            foo();
                            if (!condition) break;
                            bar();
                        }
                        

                        我现在经常这样做,因为我克服了对break 的非理性厌恶。

                        【讨论】:

                        • 确实如此,但我觉得这并不能解决问题。我并不特别关心您如何选择实施这些替代控制结构,无论是使用whilebreak 还是(喘气!)甚至goto。我想要思考代码的方式。我发现 split_while 让我以一种新的方式思考结构,类似于 while 是对条件跳转的结构改进。
                        • 这不能回答 OP 问题。
                        • Ppl 没有阅读问题。看到这个“答案”获得了最高票,这很奇怪。
                        • 也许这个答案的隐含意义是已经有足够的控制结构——当你已经有足够好的抽象时,为什么还要为它们添加更多的语言支持?
                        猜你喜欢
                        • 1970-01-01
                        • 2010-09-07
                        • 2011-06-04
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-04-09
                        • 1970-01-01
                        • 2011-10-03
                        相关资源
                        最近更新 更多