问题 良好实践:循环和如果声明[关闭]


(我复制/粘贴我在Codereview上发布的相同问题: https://codereview.stackexchange.com/questions/1747/good-practice-loop-and-if-statement

我想知道最佳做法是什么:

版本A:

loop1
  if condition1
    code1

  if condition2
    code2

  if condition3
    code3

或者,版本B:

if condition1
  loop1 with code1

if condition2
  loop1 with code2

if condition3
  loop1 with code3

我已经实现了版本B,因为它对我来说更具可读性,并且始终检查相同的条件似乎很荒谬。

但循环n次相同的阵列也可能被视为荒谬:)


9611
2018-04-09 18:09


起源

出于好奇,为什么这篇文章用C#,Java,PHP,Javascript和Ruby标记?这似乎是一个与语言无关的问题。 - Mike Bailey
这是完全不可知的,这就是为什么我试图从所有社区获得可见性(限制5个标签) - apneadiving
语言不可能是一个更好的标签,如果这是你想要的。 - Mike Bailey
我只是忽略它的存在!是到处张贴的问题? - apneadiving


答案:


从你正在考虑具有循环之外的条件的版本B的事实来看,我假设条件的真值不随阵列中的元素而变化。否则,你的问题没有意义。

结论

如果条件复杂使得它们相对于阵列的大小在评估中花费了很多时间,则版本B更快。如果是相反的方式,则版本A更快。

在这种情况下哪个更快应该与你应该采取的策略重合,因为在复杂结构中嵌入简单结构而不是在简单结构中嵌入复杂结构更容易理解。

说明

当条件相对于数组的大小足够简单时,迭代的成本超过评估条件的成本。如下面用ruby所述,版本B较慢。

$a = (1..10000)

def versionA
  $a.each do
    nil if true
    nil if false
    nil if true
  end
end

def versionB
  $a.each {nil} if true
  $a.each {nil} if false
  $a.each {nil} if true
end

require 'benchmark'
n = 10000
Benchmark.bmbm do|b|
  b.report('A'){n.times{versionA}}
  b.report('B'){n.times{versionB}}
end

Rehearsal -------------------------------------
A   7.270000   0.010000   7.280000 (  7.277896)
B  13.510000   0.010000  13.520000 ( 13.515172)
--------------------------- total: 20.800000sec

        user     system      total        real
A   7.200000   0.020000   7.220000 (  7.219590)
B  13.580000   0.000000  13.580000 ( 13.605983)

另一方面,如果相对于阵列上的迭代来评估条件成本更高,那么前者的效果将变得比后者更重要,并且速度将是另一种方式。

$a = (1..100)

def versionA
  $a.each do
    nil if (1..10).each{nil} && true
    nil if (1..10).each{nil} && false
    nil if (1..10).each{nil} && true
  end
end

def versionB
  $a.each {nil} if (1..10).each{nil} && true
  $a.each {nil} if (1..10).each{nil} && false
  $a.each {nil} if (1..10).each{nil} && true
end

require 'benchmark'
n = 10000
Benchmark.bmbm do|b|
  b.report('A'){n.times{versionA}}
  b.report('B'){n.times{versionB}}
end

Rehearsal -------------------------------------
A   2.860000   0.000000   2.860000 (  2.862344)
B   0.160000   0.000000   0.160000 (  0.169304)
---------------------------- total: 3.020000sec

        user     system      total        real
A   2.830000   0.000000   2.830000 (  2.826170)
B   0.170000   0.000000   0.170000 (  0.168738)

7
2018-04-09 18:31



这是一个很好的答案,非常感谢。 - apneadiving


因为你可能通常希望版本B更加明显

 if condition1
     loop1 with code1
 if condition2
     loop2 with code2
 if condition3
     loop3 with code3

这不能从A快速轻松地重构。

编辑:但是,如果循环“条件”由它的主体动态驱动,则应该使用A.需要更多关于代码语义的信息。


2
2018-04-09 18:13





好吧,我认为答案比看上去更合格。大多数编码实践都建议保持较小的代码块,因此从这个角度来看,它实际上归结为“整个块有多大”?但我会根据代码的意图考虑这一点。例如:

foreach (widgets)
    if (widget is red) put in left bin
    if (widget is blue) put in center bin
    if (widget is green) put in right bin

VS:

if (making widgets red) 
    foreach (widgets) put in left bin
if (making widgets blue)
    foreach (widgets) put in center bin
if (making widgets green) 
    foreach (widgets) put in right bin

每个结构都讲述了一个关于你的意图的不同故事。在第一种情况下,我们迭代小部件并对每个小部件做出决策,而在后一种情况下,我们在展开的循环中迭代决策并对小部件进行更改。这当然对else-if案例更有意义,但总的来说,如果你可以轻松地对一段代码做一个声明(例如,在这个循环中,我按颜色排序小部件)而不必写一个段落,那就是最易理解的代码,这是最佳实践的目标。

当然,性能也是一个问题。我想性能和最佳实践是不安的朋友。如果您正在访问数据库以进行迭代以获取每一行,或者如果迭代超过数千个条目,则有时需要编写面向意图较少的代码,以将最慢的操作减少到最小。这不是第一个考虑因素,但编写有效的代码需要人们从各个角度看问题并做出妥协,因此规则不是那么难和快。


2
2018-04-09 18:30





在方案A中,您将对循环的每次重复进行三次if检查。在方案B中,您只有三个if-check总计。在时间复杂度方面,第二个版本要好得多。


1
2018-04-09 18:25



每个循环也有一个条件。 - Bill the Lizard
是的,蜥蜴是正确的,但IMO的循环条件在大多数情况下比“业务规则”条件更简单。因此,最小化“业务规则”条件的评估可能是有意义的。 - Petar Repac


如果你必须负担两个条件呢?

如果condition1和condition2都被验证会发生什么?

我认为条件更好


0
2018-04-09 19:52