良好实践:循环和如果声明

时间:2011-04-09 18:09:15

标签: c# java php ruby language-agnostic

(我正在复制/粘贴我在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次相同的数组也可能被视为荒谬:)

5 个答案:

答案 0 :(得分:7)

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

<强>结论

如果条件复杂,以至于它们相对于阵列的大小需要花费很多时间进行评估,那么版本B会更快。如果是相反的方式,则版本A更快。

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

<强>解释

当条件相对于数组的大小足够简单时,迭代的成本超过评估条件的成本。正如下面用红宝石所观察到的,版本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)

答案 1 :(得分:2)

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

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

无法从A中快速轻松地重构。

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

答案 2 :(得分:2)

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

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案例更有意义,但总的来说,如果你可以轻松地对一段代码做一个声明(例如,在这个循环中,我按颜色排序小部件)而不必写一个段落,那就是最容易理解的代码,这是最佳实践的目标。

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

如果条件1和条件2都被证实会发生什么?

我认为条件更好