Ruby: == elsif != VS 之间的任何区别。 ==还有吗?

Ruby: any difference between == elsif != VS. == else?

如果您只是检查一个特定条件而其他一切都不是 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
  • 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

不同之处在于使用 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

如您所见,第二个生成更多指令,因此如果完成数十亿次,它会稍微慢一些。不知道是不是你说的"practicality"

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

案例案例

正如评论中所问,这是基于 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,更新了答案。