良好实践:循环和If语句

本文关键字:If 语句 循环 | 更新日期: 2023-09-27 17:49:39

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

我想知道最好的做法是什么:

版本:

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次也会被认为是荒谬的:)

良好实践:循环和If语句

考虑到版本B具有循环外的条件,我假设条件的真值不会随数组内元素的变化而变化。否则,你的问题就没有意义了。

结论

如果条件很复杂,相对于数组的大小,它们需要花费很多时间来求值,那么版本B更快。如果是相反的,那么版本A更快。

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

当条件相对于数组的大小足够简单时,迭代的代价大于计算条件的代价。如下图所示,版本B比ruby慢。

$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)

版本B更受欢迎,因为您可能通常希望

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

不能快速轻松地从a中重构。

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

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

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情况下更有意义,但一般来说,如果您可以轻松地对代码块进行声明(例如,在这个循环中,我按颜色对小部件进行排序),而无需编写段落,那么它就是最容易理解的代码,这就是最佳实践的目标。

当然,性能也是一个问题。我想性能和最佳实践是不容易相处的朋友。如果您要访问DB以获取每一行,或者如果迭代超过数千个条目,则有时需要编写较少面向意图的代码,以将最慢的操作减少到最低限度。这不是首先要考虑的,但是编写高效的代码需要从各个角度看问题并做出妥协,所以规则不是那么严格和快速的。

在场景A中,您将对每次循环的重复进行三次if检查。在情形B中,总共只有3次"如果"检查。在时间复杂度方面,第二个版本要好得多。

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

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

我认为A条件比较好