鉴于代码:
class Someone
def full_name
if false # on purpose
# We'll never reach this point because of the `false` above
first_name = "Other" # So how this code can affect
# the instance variable?
end
"#{first_name} #{last_name}"
end
def first_name
"First"
end
def last_name
"Last"
end
end
s = Someone.new
s.full_name
# => "Last"
为什么s.full_name == "Last"
。
换句话说,在我们不通过if语句之前,如何覆盖first_name
方法?...
要清楚,为什么ruby不能充当...... javascript,例如:
class Someone {
get full_name() {
if ( false ) {
// In JS, that doesn't override
// first_name
this.first_name = "Other";
}
return this.first_name + ' ' + this.last_name;
}
get first_name() {
return "First";
}
get last_name() {
return "Last";
}
}
let s = new Someone();
console.log("s.full_name() = ", s.full_name);
s.full_name
将与"First Last"
相关,而不是像红宝石那样"Last"
。
这是我努力了解红宝石,而不是责怪它! (我喜欢激情的Ruby)
非常感谢您的回答。
答案 0 :(得分:1)
我们需要追踪它以了解正在发生的事情。
class Someone
def full_name
if false # on purpose
# We'll never reach this point because of the `false` above
first_name = "Other" # <--- first_name stored as variable
end
puts '---> about to call methods first_name and last_name ... or not'
"#{first_name} #{last_name}" # <--- big decision here
puts '... which method wal called ???'
end
def first_name
puts 'passes in first_name'
"First"
end
def last_name
puts 'passes in last_name'
"Last"
end
end
s = Someone.new
puts s.full_name.inspect
执行:
$ ruby -w t.rb
t.rb:10: warning: possibly useless use of a literal in void context (because of the following puts)
---> about to call methods first_name and last_name ... or not
passes in last_name
... which method wal called ???
nil (value returned by the last puts in full_name)
那么为什么表达式"#{first_name} #{last_name}"
会调用last_name
而不调用first_name
?答案在The Pickaxe:
变量/方法歧义
当Ruby看到诸如a之类的名字时 表达式,它需要确定它是否是局部变量 引用或调用没有参数的方法。决定哪个是 在这种情况下,Ruby使用启发式。当Ruby解析源文件时,它 跟踪已分配给的符号。它假设 这些符号是变量。当它随后遇到一个 可以是变量或方法调用的符号,它会检查以查看 是否已经看到该符号的先前分配。如果是这样的话 将符号视为变量;否则,它将其视为一种方法 调用
...
请注意,不必执行赋值 - Ruby 只是必须看到它。
这正是您遇到的情况。将first_name
重命名为其他内容:
xxxfirst_name = "Other" # So how this code can affect
和... tada!
$ ruby -w t.rb
t.rb:10: warning: possibly useless use of a literal in void context
t.rb:6: warning: assigned but unused variable - xxxfirst_name
---> about to call methods first_name and last_name ... or not
passes in first_name
passes in last_name
... which method wal called ???
nil
或没有最后puts
:
$ ruby -w t.rb
t.rb:6: warning: assigned but unused variable - xxfirst_name
---> about to call methods first_name and last_name ... or not
passes in first_name
passes in last_name
"First Last"
first_name = "Other" # So how this code can affect # the instance variable?
您的代码没有实例变量(以@开头)。 first_name
定义了一个局部变量,该方法在方法结束时被丢弃。
在表达式中添加inspect
:
"#{first_name.inspect} #{last_name}"
结果现在是:
passes in last_name
"nil Last"
由于if
未执行,变量为nil
。
答案 1 :(得分:1)
正如其他人所说,行为发生在解析过程中。
要进行调查,您可以使用Ripper。
require 'ripper'
require 'pp'
pp Ripper.sexp("a = 3")
# [:program,
# [[:assign, [:var_field, [:@ident, "a", [1, 0]]], [:@int, "3", [1, 4]]]]]
pp Ripper.sexp("a = 3; a")
# [:program,
# [[:assign, [:var_field, [:@ident, "a", [1, 0]]], [:@int, "3", [1, 4]]],
# [:var_ref, [:@ident, "a", [1, 7]]]]]
pp Ripper.sexp("def a; end; a")
# [:program,
# [[:def,
# [:@ident, "a", [1, 4]],
# [:params, nil, nil, nil, nil, nil, nil, nil],
# [:bodystmt, [[:void_stmt]], nil, nil, nil]],
# [:vcall, [:@ident, "a", [1, 12]]]]]
pp Ripper.sexp("if false; a = 3; end; def a; end; a")
# [:program,
# [[:if,
# [:var_ref, [:@kw, "false", [1, 3]]],
# [[:assign, [:var_field, [:@ident, "a", [1, 10]]], [:@int, "3", [1, 14]]]],
# nil],
# [:def,
# [:@ident, "a", [1, 26]],
# [:params, nil, nil, nil, nil, nil, nil, nil],
# [:bodystmt, [[:void_stmt]], nil, nil, nil]],
# [:var_ref, [:@ident, "a", [1, 34]]]]]
您可以使用这个优秀的图书馆,并查找var_ref
和vcall
s。
答案 2 :(得分:0)
这是令人困惑的一点点Ruby的变量与方法调用歧义,但如果你知道你在寻找什么,它实际上很容易解决对于。找出被解释的内容的最简单方法是:
def full_name
p defined?(first_name)
end
在这种情况下,您获得"method"
,因为在该代码中没有理由不相信。
一旦添加了局部变量,即使是一个永远不会被使用的,解释也会发生变化,但它只会在之后更改,其中使用的位置会发生变化:
def first_name
"First"
end
def full_name
p defined?(first_name)
if (false)
first_name = :not_used
end
p defined?(first_name)
end
full_name
现在,第一个获得"method"
,第二个获得"local-variable"
,因为解释已经移位。 first_name =
的存在导致了这种扭曲。它无法在该块中撤消,您唯一能做的就是避免名称冲突。
您也可以致电local_variables
找出潜在变量的名称,因为这些变量是事先知道的。