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
,更新了答案。
如果您只是检查一个特定条件而其他一切都不是 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
,更新了答案。