未定义的本地方法变量如何覆盖方法?

时间:2017-10-22 14:08:11

标签: ruby

鉴于代码:

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)

非常感谢您的回答。

3 个答案:

答案 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_refvcall 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找出潜在变量的名称,因为这些变量是事先知道的。