Ruby 在条件语句中声明变量

Ruby 在条件语句中声明变量,ruby,syntax,Ruby,Syntax,我知道整行都被解析,变量的值在put解析它之前被设置,如下所示: def get_value 42 end if value = get_value puts value end # => 42 p = 1 puts "Am i a string? #{p}" if p = "Im a confused string" # => "Am i a string? Im a confused string" 我得到以下结果,我期望: #p = "Im totally a s

我知道整行都被解析,变量的值在
put
解析它之前被设置,如下所示:

def get_value
  42
end

if value = get_value
  puts value
end
# => 42
p = 1
puts "Am i a string? #{p}" if p = "Im a confused string"
# => "Am i a string? Im a confused string"
我得到以下结果,我期望:

#p = "Im totally a string" # <--Commented.
puts "Am i a string? #{p}" if p = "Im a confused string"
# => "Am i a string? "
p
是一个
FixNum
,而不是
字符串

def get_value
  42
end

if value = get_value
  puts value
end
# => 42
p = 1
puts "Am i a string? #{p}" if p = "Im a confused string"
# => "Am i a string? Im a confused string"
发生了什么事?如果一开始不明显,那么第二段代码说明了如何插入
“我是一个混乱的字符串”
。然而,在第三个示例中,仅仅声明
p
(类型不可知)就会导致插入
“我是一个混乱的字符串”

我认为这个问题不同于但类似于这些问题:


我没有得到与您相同的结果。。。(然而,较老的ruby)

根据您引用的第一个堆栈溢出答案,在这个跟踪条件的示例中,解析器假定
变量_
。。。到目前为止还没有遇到。。。是一个方法调用

推导的经验法则是

条件中的赋值总是在执行条件代码之前执行(当然,对于前导条件和尾随条件为true)

然而。。。在尾部条件中引用未定义的变量是有问题的,即使在条件中建立赋值变量也是如此


“预期”的代码是异常的:在正常行为中,尾随条件中的赋值和/或方法在条件代码之前执行,因此如果条件代码引用的变量的内容由条件代码修改,条件代码将使用修改后的变量。

TLDR:不幸的是,您的示例有缺陷,因为您为变量选择的名称与core 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            
但是

是在
内核中定义的方法。下面是一个具有“未使用”名称和显式方法定义的示例:

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首先解析
表达式,并将
x
识别为一种方法。然后执行
if
表达式,并分配一个局部变量
x
。当评估
放置
零件后,
x
仍然引用该方法。(详情请参阅)

另一方面:

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

将变量从
p
更改停止异常


塞尔吉奥·图兰采夫的回答是“正确的”

在使用
p=
而不是
p==
时,您将赋值给
p
而不是检查相等性,这会将赋值返回到
if
,并且该值对于字符串而言始终是真实的(或者除了
false,nil
之外的任何内容。我刚开始键入。哈哈,是的,Ruby允许您使用方法成功作为条件。在这种情况下,方法是
=
。您可以随时对变量调用.class来检查它是什么类型的类。@MichaelBerkowski调用了它……您使用的是=而不是==您的问题?好的-编辑这个问题的人完全改变了我所问问题的背景,让我看起来像个白痴。我很抱歉不清楚:我理解=vs==的用法。编辑这篇文章的人对
\p=“我完全是一个字符串”发表了评论
在我提供的第二个代码块中。问题是,当预先声明
p
时,如果p=“我是一个混乱的字符串”
更改,则相同行
的结果将“我是一个字符串吗?{p}”放入。简单一点:声明变量
p
(FixNum/string等)将更改
放入的输出“我是一个字符串吗?{p}”如果p=“我是一个混乱的字符串”
你说的“正确”是什么意思,“正确”是100%正确:)啊。谢谢你的提醒。谢谢你展示了指令序列,太棒了。
def x
  123
end

puts "x is a #{defined?(x)} with value #{x}" if x = 'foo'
#=> x is a method with value 123
x = 123

puts "x is a #{defined?(x)} with value #{x}" if x = 'foo'
#=> x is a local-variable with value foo
def x
  123
end

puts "#{send(:x)} #{binding.local_variable_get(:x)}" if x = 'foo'
#=> 123 foo