我知道解析整行,并在puts
解析之前设置变量的值:
def get_value
42
end
if value = get_value
puts value
end
# => 42
我得到以下结果,我期待:
#p = "Im totally a string" # <--Commented.
puts "Am i a string? #{p}" if p = "Im a confused string"
# => "Am i a string? "
然而,这很奇怪。在单行之前声明p
会以意想不到的方式更改输出:
p = "foo" # <--Un-commented.
puts "Am i a string? #{p}" if p = "Im a confused string"
# => "Am i a string? Im a confused string"
p
是FixNum
,而不是String
以下内容:
p = 1
puts "Am i a string? #{p}" if p = "Im a confused string"
# => "Am i a string? Im a confused string"
发生了什么事?如果它最初不明显,则第二个代码块说明"Im a confused string"
如何无法插值。但是,在第三个示例中,只需声明p
(不可知类型)就会导致"Im a confused string"
被插入
我认为这个问题与这些问题不同,但相似:
答案 0 :(得分:1)
我没有得到与你相同的结果......(但是红宝石,但是)
irb(main):001 > puts "#{variable_heaven}" if variable_heaven = "pizza"
NameError: undefined local variable or method `variable_heaven' for main:Object
irb(main):002 > p variable_heaven
=> "pizza"
根据您引用的第一个堆栈溢出答案,在此尾随条件的示例中,解析器假设variable_heaven
...此时尚未遇到...是方法调用。
导出的经验法则是......
条件中的赋值总是在执行条件代码之前执行(对于前导条件(当然)和尾随条件都是如此)
然而......在尾随条件中引用未定义的变量是有问题的,即使在条件中使用赋值建立变量也是有问题的。
您预期的代码&#34;异常:在正常行为中,在条件代码之前执行赋值和/或尾随条件中的方法,因此如果条件代码引用具有由条件修改的内容的变量,则条件代码将使用变量修改。
答案 1 :(得分:1)
TLDR :不幸的是,您的示例存在缺陷,因为您为变量选择了一个与核心ruby中的现有方法冲突的名称。
正如@SteveTurczyn几分钟前提到的那样,如果变量在条件行之前未知,则将其解释为方法调用。
让我们探索一些机器代码,好吗?评论了重要的一行。
puts "Am i a string? #{myvar}" if myvar = "Im a confused string"
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] myvar
0000 trace 1 ( 2)
0002 putstring "Im a confused string"
0004 dup
0005 setlocal_OP__WC__0 2
0007 branchunless 22
0009 putself
0010 putobject "Am i a string? "
0012 putself
0013 opt_send_simple <callinfo!mid:myvar, argc:0, FCALL|VCALL|ARGS_SKIP> # call method myvar
0015 tostring
0016 concatstrings 2
0018 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0020 leave
0021 pop
0022 putnil
0023 leave
当变量在beforehands之前声明时
myvar = "Im totally a string"
puts "Am i a string? #{myvar}" if myvar = "Im a confused string"
== disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, keyword: 0@3] s1)
[ 2] myvar
0000 trace 1 ( 1)
0002 putstring "Im totally a string"
0004 setlocal_OP__WC__0 2
0006 trace 1 ( 2)
0008 putstring "Im a confused string"
0010 dup
0011 setlocal_OP__WC__0 2
0013 branchunless 27
0015 putself
0016 putobject "Am i a string? "
0018 getlocal_OP__WC__0 2 # Read variable value
0020 tostring
0021 concatstrings 2
0023 opt_send_simple <callinfo!mid:puts, argc:1, FCALL|ARGS_SKIP>
0025 leave
0026 pop
0027 putnil
0028 leave
现在,您的代码存在的问题是p
是一种存在的方法。如果您不知道,p foo
相当于puts foo.inspect
。与puts
类似,它接受灵活数量的参数(甚至零参数)并返回nil
。
puts "Am i a string? #{p}" if p = "Im a confused string"
^ call method `p` here
但是
p = "foo" # Shadow existing method `p`
puts "Am i a string? #{p}" if p = "Im a confused string"
^ get local var
if you wanted to also call the method `p`, you'd have to just through some extra hoops
or just rename the variable.
答案 2 :(得分:1)
p
是Kernel
中定义的方法。这是&#34;未使用&#34;的示例。名称和明确的方法定义:
def x
123
end
puts "x is a #{defined?(x)} with value #{x}" if x = 'foo'
#=> x is a method with value 123
这是由于Ruby的解析顺序。 Ruby首先解析puts
表达式并将x
识别为方法。然后它执行if
表达式并分配局部变量x
。在事后评估puts
部分时,x
仍然引用该方法。 (有关详细信息,请参阅Modifier if
and unless
)
另一方面:
x = 123
puts "x is a #{defined?(x)} with value #{x}" if x = 'foo'
#=> x is a local-variable with value foo
此处,x
从一开始就是一个局部变量。
请注意,两种情况都存在局部变量x
:
def x
123
end
puts "#{send(:x)} #{binding.local_variable_get(:x)}" if x = 'foo'
#=> 123 foo
答案 3 :(得分:0)
从p
更改变量会阻止异常。
Sergio Tulentsev的回答是“正确的”!