是否有科学严谨的编码风格原则研究?[关闭]


25

编码风格原则(例如单出口原则)真的是件好事吗?总是还是只是有时候?到底有什么不同?

无论您有什么意见,这些显然都是主观的问题。还是他们?

是否有人试图对编码样式原则进行客观,科学严谨的研究?

我无法想象有人会对可读性进行双盲研究,但也许可能是双重无知-将不了解所学原理的学生作为学科,并使用非程序员来管理这项研究。


5
您可能对阅读完整的代码感兴趣。一切都不是可以保证的,但是很多是可以保证的,并且您会在本书中找到有关原始数据或资源的很好概述。
deadalnix 2011年

它也高度依赖于语言,某些原则适用于特定的语言,而不适用于其他语言。例如,single-exit principle由于RAII ,这并不真正适用于C ++
Martin York


@Loki-我必须考虑一下,但不确定我是否同意。这是真的,RAII被设计在很大程度上要应付例外,这是替代出口点,但(至少对某些人),他们算作另类替代出口点-的方式,没有真正计算对单一出口的原则breakgotoreturn做。在C ++中,IOW单出口并不是绝对的,但是无论如何,这几乎是我对C和大多数其他语言的看法。但这在非严格意义上仍然有意义。
Steve314,2011年

1
@ Steve314,该文章至少是遥遥相关的-它概述了这种实验方法的设计,由于在该领域显然缺乏适当记录的实验证据,这一点非常重要。
SK-logic的

Answers:


11

我回应了deadalnix的评论:阅读Code Complete 2。作者(Steve McConnell)深入讨论了编码风格,并经常参考论文和数据。


专业软件开发的基础知识和详尽介绍,希望有一天我能找到类似的质量保证。关于防御性编程和伪代码编程的章节对我特别有用。到目前为止,关于协作开发实践的章节似乎是我所读过的所有内容中最吸引人的。
蚊蚋

我没有读过这本书,也许我应该读,但是-根据-的评论-这些参考文献是否真的科学严谨和客观?如果答案是“尽其所能”,那么需要做出什么妥协?正如我在问题中建议的那样,是否有必要用一些较弱的标准来代替双盲?
Steve314,2011年

@ Steve314:我不知道,我还没有检查来源!但是您并不总是需要严格的科学知识来建立最佳实践。有时仅需对优点和缺点进行讨论即可。
M. Dudley

@emddudley-绝对正确,但并非真的如此。
Steve314,2011年

@ Steve314:对于您来说,“完整代码”将是一个不错的起点,并且我相信它的某些参考资料将解决编码风格的科学分析问题。
M. Dudley

12

我非常怀疑是否有可能对该主题进行研究得出客观结果,并且我将一直持怀疑态度,直到获得一些令人信服的研究为止。

花费了多年阅读和编写遵循某种编码风格的代码的程序员,显然会比他们一生中第一次看到的某些完美编码风格更具可读性。

与最常见的QWERTY键入布局完全相同-很容易证明它在人机工程学方面不是很理想(您是否考虑到我们的日常便利性将TYPEWRITER一词的所有字符都放在第一行?) 。

但是,诸如Dvorak或Colemak之类的改进替代产品从未流行,而且不太可能实现。因此,人们不能更高效地使用他们-事实。即使它们在某种抽象意义上是优越的。

同样,很难找到没有事先接触过编程的学科(因为这会污染我们的研究结果),但是具有编程的才能,并且愿意参加足够长的研究时间以显示两者的短暂性。长期利益和长期利益,以便可以相互权衡……(我不知道它们是否互斥,但研究人员不能简单地假设它们从来没有)。


1

1
@Chad更鲜为人知的是Carpal X,我玩了一段时间。我发现它比Colemak更好(我的carpalx达到了90-100 wpm)。即使您不打算切换到任何奇特的布局,carpalx网站也对评估和优化键盘布局以及利用遗传算法解决此类问题都非常有趣。参见mkweb.bcgsc.ca/carpalx
Konrad Morawski 2011年

1
有时,替代方法的边际收益将足以证明采用该方法的成本。否则,我们都将仍然是汇编程序和fortran。这个答案并没有真正回答最初的问题,即实际上是否存在边际收益。在德沃夏克(Dvorak)的例子中,确实有它并且已经被证明,但是它们不足以证明学习德沃夏克(Dvorak)的好处。
杰里米(Jeremy)

@杰里米(Jeremy):“这个回答并没有真正回答最初的问题,即实际上是否存在边际收益”-OP并没有直接询问此类研究的结果,他询问是否有人试图进行此类研究,这是一个更开放的问题。我回答了几个逻辑原因,说明了为什么这在技术上会很困难,以及为什么这项研究的任何结果可能会被统计噪声严重污染。因此,如果根据您给出的理由认为我的回答没有用,我认为我被不公平地否决了。
康拉德·莫拉斯基

1
@Jeremy,这些采用成本的要点是,人们只要使用更多次品就可以使用次等工具获得更好的性能。这正是在任何试图检查其主题如何应对不同编码风格的研究中都会显示的内容。他们先前对编码样式的熟悉/不熟悉所引起的噪音将使这些样式的任何先天品质的影响相形见war。除非您通过聘请完整的初学者来平整操场。就像我在回答的最后一段中指出的那样,但这带来了实际的困难。
Konrad Morawski

4

答案是肯定的!“ break”和“ continue”是否是不好的编程习惯?是这个问题的一个子集,所以我将从一个几乎没有修改的答案开始...

您可以[重新]编写没有break语句的程序(或从循环中间执行相同操作的返回值)。但是这样做时,您可能必须引入其他变量和/或代码重复,这通常会使程序更难以理解。Pascal(1960年代后期的编程语言)非常糟糕,因此对于初学者来说尤其如此。

有一个计算机科学的结果叫做Kosaraju的控制结构层次,该历史可以追溯到1973年,并在Knuth的(更)著名论文1974年的go to语句中进行结构化编程中提到。S. Rao Kosaraju在1973年证明的不是可以在不引入额外变量的情况下,将深度为n的多级中断的所有程序重写为中断深度小于n的程序。但是,可以说这仅仅是一个理论上的结果。(只需添加一些额外的变量?!您肯定可以做到这一点,以便在stackexchange上与3K +用户形成群聚...)

从软件工程的角度来看,更重要的是埃里克·罗伯茨(Eric S. Roberts)在1995年发表的一篇题​​为“ 循环出口与结构化编程:重新开放辩论”的论文(doi:10.1145 / 199688.199815)。罗伯茨总结了他之前其他人进行的一些实证研究。例如,当要求一组CS101型学生为在数组中实现顺序搜索的函数编写代码时,该研究的作者说了以下有关使用中断/返回从顺序学生中退出的学生的以下内容找到元素后立即搜索循环:

我还没有找到一个使用[此样式]尝试编写程序的人,但该人提供了错误的解决方案。

罗伯茨还说:

试图解决问题而未使用for循环的显式返回的学生的情况要差得多:在尝试该策略的42名学生中,只有七名设法产生了正确的解决方案。该数字表示成功率不到20%。

是的,您可能比CS101的学生更有经验,但是如果没有使用break语句(或等效地从循环中间返回/转到),最终您将编写代码,尽管结构合理,但在额外逻辑方面足够多变量和代码重复,有人(可能是您自己)会在尝试遵循“正确的”编码样式的某些想法时将逻辑错误放入其中。

除了return / break-type语句外,这里还有一个更大的问题,所以这个问题比关于break的问题要宽一些。根据一些观点,异常处理机制也违反了单出口点范例。

因此,基本上任何上面提到单次退出原则在今天仍然有用的人都在反对异常处理范式,除非在最后一个链接中以极为严格的方式使用它。这些准则基本上将所有异常从函数中限制为throw(),即根本不允许传播函数间异常。使用类似C ++的语法享受新的Pascal。

我从“仅返回”的概念从何而来?该站点上的普遍观点与我在此处发表的观点相反,因此我完全理解为什么我已经被否决了,即使我是第一个回答此问题的人,实际上却提供了该问题所要求的内容:有关实际可用性测试的一些信息集中在单出口问题上。我想我不应该让知识妨碍成见,特别是在游戏化网站上。从现在开始,我将坚持编辑Wikipedia。至少有来自好消息来源的信息受到赞赏,并且假装得到消息来源支持的模糊或不正确的声明最终会被禁止。在此站点上,情况恰恰相反:未经事实证实的观点占主导地位。我完全希望mod能够删除最后一部分,但至少那个家伙会知道为什么您在这里永远失去了我的贡献。


我没有对此表示反对,但是对您说:“但是这样做可能必须引入其他变量和/或代码重复,这通常会使程序更难以理解。” 重点是主观主张。我同意添加变量或代码重复会很难理解,但可以说添加goto也很难理解,而且可以通过将重复的代码分解为一个函数来减轻重复所造成的损害(尽管IMO正在移动)调用图中的复杂性不会自动消除)。
2014年

在最后一次评论后,我才看到您对1995年论文的观点,并决定赞成-有趣的观点。我认为您的否决票可能更多,因为您的帖子很长,并且从一个主观的角度入手,因此,该挫败者可能没有读完整的书(起初与我一样)。基本上,早点介绍您的真实观点是一个好主意。
Steve314 2014年

无论如何,我认为很多人都将异常视为替代的替代出口点-因为它们是针对错误情况(某种程度上)的,它们实际上并没有计算在内。我知道这对语言文化有些敏感。在某些语言中,“异常”比名称更重要-一个例外的成功案例是有效的(并且IIRC Stroustrup说了关于C ++的类似观点,提出了关于错误是否是错误的哲学观点)。甚至有人说,异常只是只要它提供了您需要的控制流,就可以使用另一个控制流。
Steve314 2014年

1
@ Steve314“ 加上可以通过将重复的代码分解为一个函数来减轻由重复造成的损害,”将该函数的逻辑放到了行外和直接视图的一部分,没有意义的部分被孤立了。更加难以理解功能的逻辑。
curiousguy18年

1
@curiousguy-是的,的确如此,这可能是我“将复杂性移入​​调用图”这一点的部分意图。我的信念是,您做出的每个选择都是一个折衷方案,因此请注意所有可能的选择以及它们的优缺点,并且知道常见的缓解措施很重要,但请注意以防万一。当然,除了折衷的一部分是您花多少时间(或浪费)在事情上做文章。
Steve314,2018年

1

http://dl.acm.org/citation.cfm?id=1241526

http://www.springerlink.com/content/n82qpt83n8735l7t/

http://ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=661092

[您的问题似乎由一个单词“是”回答。但是,有人告诉我,提供简短答案对这个问题“不屑一顾”。如果您觉得我不屑一顾,请标记答案,以便主持人可以删除它。]


1
@ luis.espinal:朝着什么目的?文字将包含哪些信息?这个问题四处徘徊。问题的哪一部分应该用一些文字解决?
S.Lott

1
作为样式问题,也许是为了提供链接摘要可以提供的更多信息(考虑到我们不知道OP是否是付费的ACM / IEEE / Springer Verlag成员,可以访问完整的文章并找到答案)他的问题。)例如,ACM文章摘要未提及编码风格。至多它讨论的是证实结构化程序定理(它本身并不讨论单次或多次返回问题)。因此,您可能已经解释了为什么该链接有意义。
luis.espinal 2011年

1
据我所知,第三篇文章(很幸运,我确实可以使用IEEE Xplore)似乎与OP的要求无关。请记住,这是一篇很棒的文章,我正在打印该文章,以供日后阅读。因此,也许您也可能已经解释了这篇文章如何帮助OP回答他的问题。总体而言,您似乎只是将许多链接放在一起。这不是一种不屑一顾的方式(除非您打算这样做),但是我再一次看不到这对OP的帮助。这就是为什么张贴者应在其链接上添加一些文本的原因。所以现在您知道我为什么要说;)
luis.espinal 2011年

1
从OP的口中Is a coding style principle - e.g. the single-exit principle - really a good thing?得知-这为他提出的有关编码样式的问题提供了背景信息。此外,编码风格与编程方法不同,特别是IEEE文章重点关注的高级设计方法(作者明确指出)。这就是为什么我说“不”的原因-范围完全不同。
luis.espinal 2011年

1
我怀疑OP的来源。他清楚地说明了编码样式(不是方法),尤其是单收益与多收益。我不得不用编写良好的,固有的不言而喻的代码来解决这几次,使用多个return语句使用一次返回将它们重写为更复杂的版本(特别是在大型的红色磁带中)按照“过程”。一个人想知道(以及有证据证明的挑战)这种任意任务的有效性,可用性和成本效益。强制执行此类命令的人仍然生活在60年代:/
luis.espinal,2011年

1

是编码风格的原则-例如单出口原则

1960年代后期,仍然对是否要退出单一出口或多次出口的人们仍然束手无策。那时,这样的讨论很重要,因为我们还处于结构化程序员的初期,并且有很多阵营宣称Bohm-Jacopini结构化程序定理的发现并不普遍适用于所有编程结构。

这应该早就解决。嗯,它已经解决了(确切地说,在学术界和整个行业都是近四个世纪),但是人们(绝对赞成或反对的人)却没有引起注意。

至于我的其余答案,都是相对的(软件中没有什么?):

  • 真的是一件好事吗?

是。一般情况下,大部分时间都是针对极端情况的警告和针对特定语言的编程结构。

总是还是只是有时候?

大多数时候。

到底有什么不同?

要看。

可读代码与不可读代码。复杂度的增加(我们现在应该知道,这增加了引入错误的可能性)与更简单的复杂度(因此,错误的可能性更小)。编译器未添加隐式返回的语言(例如Pascal,Java或C#)以及默认为int(C和C ++)。

最后,这是一项需要在键盘后面进行人工/小时训练的技能。有时,可以有多个return语句,例如此处(在某些Pascal'esque伪代码中):

function foo() : someType
  begin
  if( test1 == true )
  then
    return x;
  end
  doSomethignElseThatShouldnHappenIfTest1IsTrue();
  return somethingElse();
end;

目的很明确,该算法足够小且不够复杂,因此不能保证创建一个“标志”变量,该变量保留单个返回点中使用的最终返回值。该算法可能有错误,但其结构非常简单,以至于检测错误的工作(很可能)可以忽略不计。

有时不是(这里使用类似C的伪代码):

switch(someVal)
{
case v1 : return x1;
case v2 : return x2:
case v3 : doSomething(); // fall-through
case v4: // fall-through
case v5: // fall-through
case v6: return someXthingie;
...
...
default:
   doSomething(); // no return statement yet
}

在此,该算法不具有简单的结构,并且switch语句(一种C风格的语句)允许使用掉落步骤,这些掉落步骤可能会或可能不会有意作为算法的一部分进行。

也许算法是正确的,但是写得不好。

或者,也许是由于程序员无法承受的外力,这是合法需要的算法的实际(正确)表示。

也许是错的。

揭露任何事实的真相需要付出更多的努力比上一个示例。这里是我坚信的东西(请注意,我没有正规的研究来证明这一点):

假设假定代码片段正确:

  1. 如果代码段代表具有固有简单流结构的简单算法,则多个return语句可提高此类代码段的可读性和简便性。简而言之,我并不是小意思,而是天生的可理解自证,即不需要过多的阅读努力(也不会诱使人们呕吐,咒骂某人的母亲或在他们必须阅读时吞下子弹。 )

  2. 如果返回值是在整个算法执行期间计算的,或者算法中负责计算返回值的步骤可以在算法结构内的一个位置组合在一起,则单个返回语句可提高该代码段的可读性和简洁性。

  3. 如果单个返回语句需要对一个或多个标志变量进行赋值,则会降低该代码段的可读性和简单性,并且这种赋值的位置在整个算法中并不统一。

  4. 如果返回语句在整个算法中分布不均匀,并且如果它们在大小或结构上不统一的相互排斥的代码块之间有界线,则多个返回语句会降低此类代码的可读性和简洁性。

这与所讨论的代码片段的复杂性密切相关。而这又与圈数和霍尔斯特德复杂度度量有关。由此,可以观察到以下情况:

子例程或函数的大小越大,其内部控制流结构就越大,越复杂,并且您面临的问题是使用多个还是单个返回语句的可能性就越大。

得出的结论是:保持函数的小型化,一件事情只能做一件事情(并且要做好)。如果它们名义上具有较小的圈数和半数复杂度指标,那么它们不仅必定是最有可能正确的,而且是可理解的任务的执行,它们的内部结构也将相对不言而喻。

然后,只有这样,您才可以很容易地在不损失大量睡眠的情况下,决定是否使用一次收益和多次收益,而不会冒着任何选择引入错误的巨大风险。

人们还可以仔细研究所有这一切,并提出建议,当人们在面对单一收益或多重收益的问题时感到挣扎时,这是因为-由于缺乏经验,愚蠢或缺乏职业道德,他们没有编写干净的代码并且倾向于编写可怕的功能,完全不考虑环行和霍尔斯特措施。


1
C ++返回类型不是默认为int:没有默认返回类型,因此必须在所有情况下都指定它。
Sjoerd

从我写这个问题开始- 程序员 .stackexchange.com / questions / 58237 /…。基本上,我提倡对此原则有所了解,但并不严格遵循该原则-如果所有退出点都是显而易见的,那么我很高兴。我在这里的观点-仅仅因为我提到一个原则作为示例并不意味着我提倡该原则,当然也不是严格的形式。不过,我的主观意见只是-我的观点可能有更强的论点,或者我强烈认为是错误的。
Steve314,2011年

什么是“默认为int”?
curiousguy18年

我的意思是说,并且我应该对此进行限定,如果代码恰好具有不带显式返回值的执行分支,那么大多数编译器将简单地“推”累加器寄存器的值作为返回值。这实际上意味着以int形式返回上一次算术运算的结果(可能存在的任何垃圾)。不管该函数最初打算做什么,那肯定是垃圾(以及ergo,未定义的行为)。C和C ++可以警告您,但是编译可以让您编译,除非您使用-Werror或类似的东西。
luis.espinal
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.