有用的替代控制结构?

有时当我编程时,我发现一些特定的控制结构对我来说是非常有用的,但是不能直接在我的编程语言中使用。 我想我最常见的愿望就像是“分裂”(我不知道该怎么称呼这个):

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

这个代码的语义是foo()总是运行,然后检查条件。 如果为true,那么bar()运行,我们回到第一个块(从而再次运行foo()等)。 感谢reddit用户zxqdms发表的评论 ,我了解到Donald E. Knuth在他的论文“结构化编程go to声明” (参见第279页)中提到了这个结构。

你认为什么替代控制结构是组织计算的有用方法?

我的目标是给自己和其他人一些思考结构化代码的新方法,以便改进分块和推理。

注意 :我不是问如何概括所有可能的控制结构,无论是使用jneif / goto ,Lispmacros,continuations,monads,combinators,夸克还是别的。 我在问什么专业化在描述代码中很有用。

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

 forever { // ... } 

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

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

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

循环与其他:

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

大多数语言都有内置的函数来覆盖常见的情况,但是“fencepost”循环总是一个杂项:循环,你想在每次迭代中做一些事情, 迭代之间也做一些事情。 例如,使用分隔符连接string:

 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 += ", "; } 
 { foo(); } split_while( condition ) { bar(); } 

您可以使用常规方式轻松完成此操作:

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

我现在这样做的频率非常高,因此我克服了我非常不喜欢的break

如果你看看Haskell,尽pipe对于不同的控制结构有特殊的语法,但是控制stream经常被types捕获。 最常见的这种控制types是Monad,Arrows和applicative functors。 所以如果你想要一个特殊types的控制stream,通常是某种高阶函数,你可以自己写,或者在Haskells包数据库(Hackage)中find一个相当大的函数。

这些函数通常位于Control命名空间中,您可以在其中find并行执行error handling的模块。 通常在过程语言中find的许多控制结构在Control.Monad中有一个函数对应,其中包括循环和if语句。 如果else是haskell中的keywordedexpression式,如果没有else在expression式中是没有意义的,但是在monad中是完全意义的,那么if和else之间没有else的if语句被捕获。

另一个常见的情况是在更一般的上下文中进行列表操作。 function语言是相当喜欢fold ,专业版本,如mapfilter 。 如果你有一个monad,那么就有一个fold的自然延伸。 这就是所谓的foldM ,因此也有可以想到的任何折叠的特殊版本的扩展,比如mapMfilterM

用(lisp样式)macros,tail-calls和continuations,这些都是古怪的。

使用macros,如果标准控制stream构造对于给定的应用程序是不够的,程序员可以自己编写(还有更多)。 这只需要一个简单的macros就可以实现你给出的构造。

通过尾部调用,可以将复杂的控制stream程模式(如实现状态机)分解为函数。

Continuations是一个强大的控制stream原语(try / catch是它们的受限版本)。 结合尾部调用和macros,复杂的控制stream模式(回溯,parsing等)变得直截了当。 另外,它们在网页编程中也很有用,你可以用它们反转控制的反转; 你可以有一个function,要求用户input一些信息,做一些处理,要求用户input更多信息等。

为了解释Scheme标准,您应该设法消除使其他function显得有必要的限制,而不是将更多function添加到您的语言中。

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

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

ALSO条件总是被评估。 ELSE像往常一样工作。

它也适用于案件。 也许这是一个消除断言的好方法:

 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 

我不知道这是否有用或简单的阅读,但它是一个例子。

如果不:

 unless (condition) { // ... } 

而不是:

 until (condition) { // ... } 

标签循环是我发现自己有时从主stream语言丢失的东西。 例如,

 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 ,然后返回到循环的开头,而不计算增量。)

我build议“那么”运营商。 它返回第一次迭代的左操作数和所有其他迭代的右操作数:

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

在第一次迭代中,它将“”添加到所有其他所添加的结果中,“”,所以你得到一个string,其中包含用逗号分隔的每个项目。

 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 

这些function只是一个阅读的forms。 他们不会创造范围。 它更像是一个gosub /从Basic返回。

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

怎么样

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

或者是这个效果 但是,我喜欢这样的想法,如果需要的话,可以使用其结果(如在颜色示例中)。

D的范围警卫是一个不常见的有用的控制结构。

我想我应该提到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 $} 

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

ignoring – 忽略在某个代码块中发生的exception。

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

用一个ignoring控制结构,你可以写得更简洁,更可读:

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

[Scala在它的标准库中提供了这个(和许多其他exception处理控制结构),在util.control包中。 ]

我想看到一个关键字分组输出。 而不是这个:

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

如果你有一个体面的平台,控制和/或报告格式,你将不需要写这个代码。 但是我经常发现自己做这件事真的很奇妙。 最烦人的部分是最后一次迭代之后的页脚 – 在没有重复代码的情况下,很难在现实生活中做到这一点。

一些替代的东西

 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 < N) ,但是i在循环之后超出了范围。

这是一个笑话,但你可以得到这样的行为:

 #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; } 

ps格式化这是有点困难,我绝对不满意, 但是,emacs更糟糕。 任何人都在意尝试vim?

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

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

安托确保我没有得到这个答案upvotes,我结束了任何语言,我工作在任何一段时间缺乏goto的烦恼。

 for int i := 0 [down]to UpperBound() [step 2] 

每个C语言都缺less。

在投票或写评论之前请考虑
for (int i = 0; i <= UpperBound(); i++)来说不是多余的,它具有不同的语义:

  1. UpperBound()只评估一次

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

这与@Paul Keister的回应相似。

(嘟,,咕))几年前,我正在研究的应用程序有所谓的控制中断处理的许多变化 – 所有这些逻辑,将sorting的数据行分成组和小组,包括页眉和页脚。 由于应用程序是在LISP中编写的,我们在一个名为WITH-CONTROL-BREAK的macros中捕获了常见的成语。 如果我将这种语法转换成stream行的曲线forms,它可能看起来像这样:

 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等等,这种需求似乎没有像以前那么多。 但不时,我希望我有那个控制结构。

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

“PL / I风格”为“循环范围? VB的等价物将是:

计数1,2,... 49,50,23,999,998,...,991,990
  对于I = 1至50,23,999至990步骤-1

我能看到的最常见的用法是循环运行索引列表,然后再投入一个。 顺便说一句,For-Each用法也可以得心应手:

 Bar1,Bar2,Bar3是IEnum(Wazoo);  Boz是一个Wazoo
   Bar1,Bar2,Enumerable.One(Boz),Bar3中的每个Foo作为Wazoo

这将运行Bar1中所有项目的循环,Bar2,Boz和Bar3中的所有项目。 Linq可能会允许这个没有太多的困难,但内在的语言支持可能会更有效一些。

在许多语言中没有的控制结构之一是case-in型结构。 类似于一个开关types的结构,它允许你有一个整齐的格式化的可能选项列表,但匹配第一个是真实的(而不是第一个匹配input)。 这样的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; 

它基本上是一个比开关更精确的if/elseif/elseif/else构造表示,并且可以非常方便地expression该逻辑。

如何通过一个列表迭代一个移动的窗口(n个元素而不是1个)? 我认为,这与慷慨的答案是切线相关的。

就像是

 #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 

这对于某些types的东西是有用的。 不要误解我的意思,这很容易作为一个函数来实现,但是我认为当有更多特定/描述性工具用于工作时,很多人都试图带出和循环。