在条件中声明变量

时间:2015-03-19 02:34:06

标签: ruby syntax

我知道解析整行,并在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"

pFixNum,而不是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"被插入

我认为这个问题与这些问题不同,但相似:

4 个答案:

答案 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)

pKernel中定义的方法。这是&#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的回答是“正确的”!