【问题标题】:Ruby: any difference between == elsif != VS. == else?Ruby:== elsif != VS 之间的任何区别。 == 别的?
【发布时间】:2018-06-20 19:39:05
【问题描述】:

如果您只是检查一个特定条件而其他所有条件都不是 X,那么这两个小逻辑块的结果是否有任何实际差异?

def country
    if params[:ip_country_code] == "X"
        {:api_key => 1}
    else
        {:api_key => 2}
    end
end

对比

def country
    if params[:ip_country_code] == "X"
        {:api_key => 1}
    elsif params[:ip_country_code] != "X"
        {:api_key => 2}
    else    
    end
end

【问题讨论】:

  • elsif你可以检查条件,但如果你不能检查条件。

标签: ruby-on-rails ruby methods logic operators


【解决方案1】:

如果使用elsif 会产生更多的比较,因此需要处理器做更多的工作。您可以使用以下脚本对其进行反汇编:

code = <<~CODE
  a = 4
  if a == 4
    puts "equal"
  else
    puts "not equal"
  end
CODE

code2 = <<~CODE
  a = 4
  if a == 4
    puts "equal"
  elseif a != 4
    puts "not equal"
  end
CODE

puts RubyVM::InstructionSequence.compile(code).disasm
puts "\n\n----------------------\n\n"
puts RubyVM::InstructionSequence.compile(code2).disasm

第一个结果(if-else):

== disasm: #<ISeq:<compiled>@<compiled>>================================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] a
0000 trace            1                                               (   1)
0002 putobject        4
0004 setlocal_OP__WC__0 2
0006 trace            1                                               (   2)
0008 getlocal_OP__WC__0 2
0010 putobject        4
0012 opt_eq           <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
0015 branchunless     26
0017 trace            1                                               (   3)
0019 putself
0020 putstring        "equal"
0022 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0025 leave                                                            (   2)
0026 trace            1                                               (   5)
0028 putself
0029 putstring        "not equal"
0031 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0034 leave

而对于第二个(if-elsif):

== disasm: #<ISeq:<compiled>@<compiled>>================================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] a
0000 trace            1                                               (   1)
0002 putobject        4
0004 setlocal_OP__WC__0 2
0006 trace            1                                               (   2)
0008 getlocal_OP__WC__0 2
0010 putobject        4
0012 opt_eq           <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
0015 branchunless     51
0017 trace            1                                               (   3)
0019 putself
0020 putstring        "equal"
0022 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0025 pop
0026 trace            1                                               (   4)
0028 putself
0029 getlocal_OP__WC__0 2
0031 putobject        4
0033 opt_neq          <callinfo!mid:!=, argc:1, ARGS_SIMPLE>, <callcache>, <callinfo!mid:==, argc:1, ARGS_SIMPLE>, <callcache>
0038 opt_send_without_block <callinfo!mid:elseif, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0041 pop
0042 trace            1                                               (   5)
0044 putself
0045 putstring        "not equal"
0047 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0050 leave                                                            (   2)
0051 putnil                                                           (   5)
0052 leave

如您所见,第二个生成的指令更多,因此如果执行数十亿次,它会稍微慢一些。不知道是不是你说的“实用性”。

在您的情况下,从哈希中访问值将会产生更多开销。如果你在条件下执行一些真正“繁重”的操作,它会重复两次并且会对性能产生真正的影响。

案例案例

根据 cmets 的要求,这是基于 case 的解决方案的版本:

code3 = <<~CODE
  a = 4
  case a
  when 4 then puts "equal"
  else puts "not equal"
  end
CODE

结果:

== disasm: #<ISeq:<compiled>@<compiled>>================================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 2] a
0000 trace            1                                               (   1)
0002 putobject        4
0004 setlocal_OP__WC__0 2
0006 trace            1                                               (   2)
0008 getlocal_OP__WC__0 2
0010 dup
0011 opt_case_dispatch <cdhash>, 21
0014 dup                                                              (   3)
0015 putobject        4
0017 checkmatch       2
0019 branchif         31
0021 pop                                                              (   4)
0022 trace            1
0024 putself
0025 putstring        "not equal"
0027 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0030 leave
0031 pop                                                              (   5)
0032 trace            1                                               (   3)
0034 putself
0035 putstring        "equal"
0037 opt_send_without_block <callinfo!mid:puts, argc:1, FCALL|ARGS_SIMPLE>, <callcache>
0040 leave

结论:这只是比普通的if-else长一点,但还是比if-ifelse好很多。

编辑:我还注意到反汇编 if-else 的错误,更新了答案。

【讨论】:

  • 令人着迷。您可以使用他的回答中建议的案例版本@mudasobwa 来做到这一点吗?只是为了看看。
  • 酷,谢谢。所以就速度而言,如果担心的话,那么 1) if-else, 2) case, 3) if-elsif。令人着迷。
【解决方案2】:
  • if 是好的或两个互斥的选择。
  • elsif 通常用于不同条件的多个(或两个不互斥的)选择。
  • 相同条件的多项选择 通常包含在with 子句中。

虽然它们之间没有逻辑上的区别,但正确的写法是:

def country
  value =
    case params[:ip_country_code]
    when "X" then 1
    else 2
    end

  # or:
  value =
    if params[:ip_country_code] == 'X'
      1
    else
      2
    end

  # or even with ternary:
  value = params[:ip_country_code] == 'X' ? 1 : 2

  {api_key: value}
end

【讨论】:

  • 谢谢 mudasobwa。也看看@katafrakt 的回答。总而言之,这很有趣。
  • 考虑说明您的偏好。我的是{ api_key: params[:ip_country_code] == 'X' ? 1 : 2 }。另外,我们不需要params 作为参数吗?
  • 现在我总是选择case,因为迟早我们需要扩展条件来添加新的子句。
猜你喜欢
  • 2012-04-21
  • 2016-09-07
  • 2013-08-05
  • 2021-09-20
  • 2012-03-23
  • 1970-01-01
  • 1970-01-01
  • 2011-07-28
  • 2013-05-26
相关资源
最近更新 更多