在什么时候禁忌在循环中循环?


23

只是好奇。我曾经遇到过的最多的是for循环中的for循环,因为从Linus Torvalds读取以下内容后

制表符是8个字符,因此缩进也是8个字符。有些异端运动试图使缩进量增加4个(甚至2个!)字符,这类似于试图将PI的值定义为3。

原理:缩进的整个思想是明确定义控制块的开始和结束位置。尤其是当您连续20个小时查看屏幕时,如果压痕较大,则发现压痕的工作原理要容易得多。

现在,有些人会声称具有8个字符的缩进会使代码向右移得太远,并使得在80个字符的终端屏幕上难以阅读。 答案是,如果您需要三个以上的缩进级别,那么无论如何您都会被搞砸,应该修复程序。

https://www.kernel.org/doc/Documentation/CodingStyle

我认为进入第三层循环对我来说是不可接受的做法,并且会重组我的代码(Primally Qt)。

莱纳斯在开玩笑吗?

是否取决于语言或应用程序?

是否有某些事情绝对需要三级或更多级循环?


8
我很困惑,为什么您从缩进跳转到循环级别?您在讨论缩进时有大量引文,然后突然出现了有关嵌套循环的问题。
Pieter B

5
Linus在该部分可能不是(只是)在开玩笑,但是请注意,这只是一个样式指南,并且相同的样式指南强调“内核编码样式非常简单”,即比其他样式更是如此。

5
@Akiva如果没有4个嵌套循环,就无法通过4维矩阵。我发现有人会限制您可以拥有的嵌套循环的数量真是太疯狂了。莱纳斯显然是非常笼​​统的人,你不应该把你读的一切都当作圣经。
Alternatex

9
@Alternatex您需要4个循环并不意味着它们必须按词法嵌套。从引用中可以明显看出,我们在谈论如何组织代码,而不是执行。

4
@delnan我并不是说4个嵌套循环在视觉上令人愉悦,而且我知道还有其他方法可以解决,但我发现OP如此真实地理解Linus的话是很愚蠢的。第四级缩进=世界末日。休息一下
Alternatex

Answers:


19

内核强烈喜欢简单的算法

尽管各种算法可能需要在循环内深层嵌套的循环,但是在Linux内核(其中引用了报价)的上下文中,您通常需要快速的实时响应。在这种情况下,深层嵌套是一种气味,可能表明该域的代码流过于复杂,并且可能由于其执行特性而不是可读性或缩进问题而需要更改。

此外,Linux内核在可审核性和测试要求方面与大多数应用程序代码不同-因此,宁愿在单个功能中不要使用4级以上的嵌套算法。它应该是明显的看出每个代码片段做什么准确和详细,包括所有可能的控制流和边缘情况。深层嵌套的代码阻碍了这一点。


因此,您是否认为对于C等较低级别的语言,深层嵌套的循环通常是更多的禁忌because项目,这些项目使用较低级别的语言得益于专注于更简单算法的编码风格?
Akiva 2015年

4
@Akiva我不会将它绑定到较低级别的语言或C上,而是绑定到代码领域。我认为,类似的准则在编写必须健壮,注重安全性和可审计性(以其他方式为代价)的代码时适用于任何语言。例如,用Java或Haskell编写的加密库还应该以一种使事情变得尽可能简单,限制嵌套,并试图将所有内容分离为易于分析并具有所有可能后果的块的方式编写。
Peteris

一个非常有见地和有用的评论/答案。只是好奇; 今天完成的哪种项目使用低级语言,不会专注于健壮,可审计和安全的项目?
Akiva 2015年

7
例如,@ Akiva是机器学习代码,您可能仅出于性能原因而希望使用C,但并不十分在乎鲁棒性或安全性,因为它将在受控条件下在内部运行。另外,在小型嵌入式微控制器上实现简单的业务功能-实际上,这通常具有像关注功能和开发速度这样的业务,但以质量和安全性为代价,但是使用的是低级语言。
彼得尼斯(Peteris)2015年

49

在某种程度上,我停止认真对待“制表符是8个字符”的引用。制表符的全部要点是它们不是固定数量的字符(如果有的话,制表符是一个字符)。真是费劲。同样,我也不完全相信为“三个缩进级别”设置严格的规则是明智的(与为任何事物设置严格的规则一样合理)。

然而,限制你的缩进的水平在一般合理的建议,而不是一个应该作为一个惊喜给你。

最终,如果您的程序需要三个迭代级别,那么这就是您的程序所需要的。引用的精神不是神奇地减轻项目中的要求,而是将逻辑分解为函数和类型,以使代码更简洁,更具表现力。

这只是反馈到与上述缩进级别相同的准则中。这是关于如何构造代码并使代码在未来几年保持可读性,可维护性和趣味性的。


6
我相信制表符是8个字符的“声明”是专门针对内核开发的。该引用摘自特定项目的编码指南,并不旨在用作一般用途的指南,因此,希望对此有充分的看法。
Lie Ryan

6
@LieRyan:那还是很麻烦-任何东西的编码指南都没有规定我设置标签的宽度!但是我怀疑莱纳斯知道这一点。
与莫妮卡(Monica)进行轻度比赛

6
当然,它是依赖于语言的-在c#中,通常在名称空间,类和方法中缩进。.在谈论控制流语句主体之前,您已经处于3个缩进级别缩进。
PeterL 2015年

3
@LightnessRacesinOrbit我将“制表符是8个字符”解释为不是意味着您必须亲自在编辑器中查看8个宽度的制表符,而是出于样式指南中其他规则的目的(例如“行数限制)是80列,这是一个强烈推荐的限制。”)必须将制表符视为8列,这也与其他有关函数调用中参数对齐的规则有关。再说一次,我认为那行的意图根本没有强迫您以这种方式查看选项卡,我之前已经用4个宽选项卡完成了内核修补,最后对代码进行了重排。

4
@underscore_d:看来我错了:Outside of comments, documentation and except in Kconfig, spaces are never used for indentation, and the above example is deliberately broken.-从OP中的引号开始向下6段。
slebetman

16

关键与任何流控制构造都相同:如果代码难以理解,则需要对其进行重构。如果您要对多维数组进行一些简单的操作,则只要嵌套最深层的逻辑简单明了,就可以将循环嵌套5或6个深度。但是,如果您正在处理一些复杂的业务逻辑,而循环的主体是十几行或更多行,那么您可能不希望嵌套多于一个的循环。您可以尝试计算代码的循环复杂性,但实际上归结为所讨论代码的可读性和可维护性。


11
究竟。很难说托瓦尔兹是懒人。(当然,他是。)他可能对您的品味太死板,但他描述的是引起实际问题的真正发展关注点。您不必完全按照他说的去做,但您应该考虑一下他为什么这么说。
罗杰罗杰(Roger Roger)2015年

7
@ScantRoger实际上,如果您不懂得Torvalds的幽默感,那听起来就太死板了。我记得,在同一文档的前面,他建议打印出一份GNU编码风格指南,仅在某种形式的仪式上进行烧录。您几乎不会认真对待,是吗?在本文中,他的主要观点是将linux内核的缩进定义为8个空格,仅此而已,这就是他所坚持的。最后一句话只是为了强调这一点,并不是说您不能使用更多级别的缩进-并不意味着僵化。
cmaster 2015年

1
@cmaster感谢您提供上下文信息!在回答您的查询时,我几乎不重视任何事情。;)
Scant Roger 2015年

2
@cmaster,然后读取他对github pull请求的响应和提交消息的行长。他是个疯子。
古斯多

3
礼节性地刻录GNU编码准则实际上可能不是必需的,但是在任何时间点都是完全可以做到的。
dmckee 2015年

13

莱纳斯在开玩笑吗?

文章以俏皮的风格写成,暗示作者熟悉严肃的从业者讨论编码风格的方式:我们都有我们的喜好,我们为之疯狂地捍卫着他们,但舌头至少部分贴在脸颊上。我们非常了解,其中很多只是个人喜好问题。他说了这么多话,"Coding style is very personal, and I won't _force_ my views on anybody"至少在他个人维护的代码之外。但是给定项目中样式的一致性是一个非常好的主意。我宁愿编写自己不喜欢的样式的代码,也不愿在给定的函数中处理多种样式。

这是一个清晰有趣的示例:

However, there is one special case, namely functions: they have the
opening brace at the beginning of the next line, thus:

int function(int x)
{
    body of function
}

Heretic people all over the world have claimed that this inconsistency
is ...  well ...  inconsistent, but all right-thinking people know that
(a) K&R are _right_ and (b) K&R are right.  Besides, functions are
special anyway (you can't nest them in C).

好玩的(1)。

尝试避免缩进以免失控是一个很好的建议,尽管三级最大值可能是双曲线的。我不会grep内核源代码并计算四个制表符的序列,但是我敢打赌,您至少可以找到Torvalds编写的一个。

另一方面,如果某人可以编写Linux内核而不会经常缩进三级,那么三级限制可能是一个值得在您自己的代码中尝试一会儿的练习,只是看看它会花些什么。你知道,这不像是改变性别。这不是一生的承诺。

如果您在Internet上遇到某个认为他比Torvalds(2)更了解编程的人,那么您知道哪种人喜欢在Internet上大声说话。

另一方面,他在八个空格的标签上犯了错误的罪行。那是一个男人的狂欢,他应该保持克制并通过狭槽喂养。四个空格显然是正确的。

(1)但是请注意他是如何错误地在椭圆前面放一个空格,在椭圆后面放两个空格,以及句号之后放两个空格。错,错,错。然后他有胆大妄为要谴责异端。异端是你,托瓦尔兹!是你!

(2)如果您想谈论“ 了解如何设计源代码控制系统 ”,可能会有一些争论的余地。

注意:亲爱的用户,它反复提交了相同的编辑:引用的材料中的格式与作者原本打算的一样。这是因为这是一篇有关定宽文本格式的文章,该文章是由写了定宽文本格式的人给出的,这些人对定宽文本格式的设置有相当多的想法。格式是作者意图的有意识的和有意的部分,与主题有关。

另外,我在自己的文字中提到了这种格式。如果您进行了预格式化,则我的脚注(1)变得乱七八糟。如果取消了预格式化,则我的脚注(1)中的文本也应该这样,指的是句尾句号后的空格对。我可以看到删除该脚注的基本原理,因为它不像我编写时看起来那么有趣。但是在不删除脚注的情况下删除格式无济于事。


3
精彩回答。其中一种情况应为+2 ...(注意:.此注释中没有错误的空格;
cmaster 2015年

2
您指出的Linus简介部分非常重要,因此谢谢您!我认为第一句话对上下文也非常重要,特别preferred coding stylebut this is what goes for anything that I have to be able to maintain
克里斯·哈斯

9

Linus的说话风格很直率,幽默感很干,但是在这种情况下他并不是在开玩笑。在某些情况下,算法需要嵌套两个层次以上的深度,但是您可以使用除了缩进代码之外的其他方法来实现此目的。Linux内核样式指南强烈建议使用其他方法,因为它们难以维护深度嵌套的循环,这就是Linus在这里所说的。

对于替代方法的一些示例,您可以使用递归,将内部循环拆分为它们自己的函数,或建立中间数据结构。

过多的嵌套是较容易编写但较难阅读的情况之一。设置较大的制表符深度是Linus使其更烦人的书写方式。


3

在许多问题中,提出建议的人与提出问题的人的建议有所不同。如果您问“我是否应该将循环嵌套的深度超过两个级别”,那么对于您(问这个问题的人),答案是否定的。如果您询问,那就不要这样做。如果您有足够的经验不需要询问,那么您知道每种情况下正确的答案是什么。也不要争论您是否同意答案,因为答案不适合您。


1

这似乎是教科书中尾巴摇狗的情况。

如果你有80个字符的显示器,那么你当然会尝试使代码尽可能地合适,即使它不能产生最佳的代码结构

解决剩下的问题:

我认为这是不可接受的做法。

我认为您对此读得太多了。抵制在没有正确理解上下文的情况下将阅读的所有内容当作福音的冲动。

他在开玩笑吗?

很难确定上下文,但是请参见上面的原始观点。

是否取决于语言或应用程序?

非常非常。使用可能在终端(或终端仿真器)上编码的任何大型机/中型语言。

是否有某些事情绝对需要三级或更多级循环?

是的,在某些蛮力算法中非常常见。请参阅关于Euler项目的问题31。这是一个经典问题示例,可以使用蛮力通过使用多个循环(确切地说是8个)来解决。


1
看起来问题31不需要暴力破解,可以使用动态编程算法来解决(编辑:这意味着,如果您使用暴力破解算法,则代码结构不是最佳的)。另外,Linus的观点是,如果您的代码需要许多级别的缩进,则可能不是代码的最佳结构。
Vincent Savard 2015年

2
@VincentSavard从未说过需要蛮力。反对您的第二点-有时这是最清晰,最简洁的方法,更不用说在某些情况下最有效的方法了。
罗比·迪

1
遇到这种问题,我通常不会缩进循环。我认为我有一个案例,其中包含20个嵌套循环,编写起来绝对是微不足道的,而且没有缩进,因此您可以看到循环几乎相同。
gnasher729 2015年

1
@RobbieDee:我的观点是,您通过多个循环解决问题的示例是,您的算法不如动态编程解决方案有效,后者不需要那么多的缩进级别。因此,正如Linus所说,可以通过使用更好的解决方案来消除压痕级别。您也误解了我的第二点,因为我同意你的意思。有时,这是最好的解决方案。有时并不常见,也不太可能。
Vincent Savard 2015年

1
Linus的引言几乎明确地指出,如果某些代码需要像问题31这样的暴力破解,那么无论如何您都会被搞砸了-它既不快也不简单,内核操作也必须既快又简单。在内核中包含任何O(n ^ 4)算法都会带来很大的性能风险或拒绝服务问题,因此在此情况下,建议仅警告说这是代码的征兆,在Linux中可能根本不适合使用。
Peteris

0

莱纳斯在开玩笑吗?

不,这些是官方指南。

是否取决于语言或应用程序?

编码准则通常取决于语言和应用程序,但是深层嵌套的代码总是使读者费劲。

嵌套代码的问题在于,通常它会增加循环复杂性:也就是说,代码嵌套越多,函数中存在的潜在执行路径就越多。潜在执行路径的组合爆炸使代码难以推理,因此通常应避免使用。

那么为什么3?主观编码准则既难以执行,也无法自动执行。在最大缩进级别上建立客观的编码准则需要达成一致:在Linux内核中,他们选择了3。

它是任意的,显然对他们来说足够了。

是否有某些事情绝对需要三级或更多级循环?

从算法角度来说,也许,但是在表达能力足够强的语言中,您始终可以将代码重构为较小的块(无论是函数还是闭包)。

显然,您可以编写几乎没有嵌套的混淆代码,并且可以互相调用许多小函数,而无需拼写合同。

...但是,具有明确合同的小型职能部门通常比具有明确合同的大型职能部门更容易审计。


2
尽管这可能是官方指南,但在内核代码中未执行该指南的地方很简单。
MikeB 2015年

1
@MikeB:还有更多理由自动执行准则...
Matthieu M.

1
@MatthieuM。您确定您了解准则和强制性要求之间的区别吗?作为一般的“经验法则”(如果您愿意的话,是一条准则),准则更像是建议,并不强制执行。
布伦丹2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.