77

Ruby setter——无论是由其创建还是手动创建——似乎是在类本身中访问时(c)attr_accessor唯一需要限定的方法。self.这似乎使 Ruby 在语言世界中独树一帜:

  • 所有方法都需要self/ this(比如 Perl,我认为是 Javascript)
  • 没有方法需要self/ thisis (C#, Java)
  • 只有二​​传手需要self/ this(Ruby?)

最好的比较是 C# 与 Ruby,因为这两种语言都支持访问器方法,这些方法在语法上就像类实例变量一样工作:foo.x = y, y = foo.x. C# 称它们为属性。

这是一个简单的例子;Ruby 和 C# 中的相同程序:

class A
  def qwerty; @q; end                   # manual getter
  def qwerty=(value); @q = value; end   # manual setter, but attr_accessor is same 
  def asdf; self.qwerty = 4; end        # "self." is necessary in ruby?
  def xxx; asdf; end                    # we can invoke nonsetters w/o "self."
  def dump; puts "qwerty = #{qwerty}"; end
end

a = A.new
a.xxx
a.dump

带走self.qwerty =()它会失败(Linux 和 OS X 上的 Ruby 1.8.6)。现在 C#:

using System;

public class A {
  public A() {}
  int q;
  public int qwerty {
    get { return q; }
    set { q = value; }
  }
  public void asdf() { qwerty = 4; } // C# setters work w/o "this."
  public void xxx()  { asdf(); }     // are just like other methods
  public void dump() { Console.WriteLine("qwerty = {0}", qwerty); }
}

public class Test {
  public static void Main() {
    A a = new A();
    a.xxx();
    a.dump();
  }
}

问:这是真的吗?除了 setter 之外,还有其他需要 self 的场合吗?即,是否存在其他情况下不能在没有self的情况下调用Ruby 方法?

当然有很多情况下 self变得必要。这不是 Ruby 独有的,只是为了清楚:

using System;

public class A {
  public A() {}
  public int test { get { return 4; }}
  public int useVariable() {
    int test = 5;
    return test;
  }
  public int useMethod() {
    int test = 5;
    return this.test;
  }
}

public class Test {
  public static void Main() {
    A a = new A();
    Console.WriteLine("{0}", a.useVariable()); // prints 5
    Console.WriteLine("{0}", a.useMethod());   // prints 4
  }
}

同样的歧义以同样的方式解决。但是,虽然我问的是微妙的情况

  • 已经定义了一个方法,并且
  • 没有定义局部变量,并且

我们遇到

qwerty = 4

这是模棱两可的——这是一个方法调用还是一个新的局部变量赋值?


@迈克·斯通

你好!我理解并欣赏你提出的观点,你的例子很棒。相信我,如果我有足够的声誉,我会投票赞成你的回应。然而我们仍然不同意:

  • 在语义问题上,以及
  • 在一个事实的中心点上

首先,我声称,并非没有讽刺意味,我们正在就“歧义”的含义进行语义辩论。

当谈到解析和编程语言语义(这个问题的主题)时,你肯定会承认“歧义”这个概念的广泛范围。让我们采用一些随机符号:

  1. ambiguous:词汇歧义(lex must 'look ahead')
  2. 歧义:语法歧义(yacc 必须遵循解析树分析)
  3. AMBIGUOUS:歧义在执行的那一刻知道一切

(而且 2-3 之间也有垃圾)。所有这些类别都是通过收集更多的上下文信息来解决的,越来越全球化。所以当你说,

当没有定义变量时,“qwerty = 4”在 C# 中是 UNAMBIGUOUS...

我完全同意。但出于同样的原因,我要说

“qwerty = 4”在红宝石中是不明确的(因为它现在存在)

“qwerty = 4”在 C# 中是不明确的

而且我们还没有互相矛盾。最后,这是我们真正不同意的地方:如果没有任何进一步的语言结构,ruby 可以或不能实现,例如,


对于“qwerty = 4”,如果没有定义局部变量,ruby UNAMBIGUOUSLY 会调用现有的 setter

你说不。我说是; 另一个 ruby​​ 可能存在,它在各个方面的行为都与当前完全相同,除了“qwerty = 4”在不存在 setter 和 local 时定义了一个新变量,如果存在则调用 setter,如果存在则分配给 local。我完全接受我可能是错的。事实上,我可能错的原因会很有趣。

让我解释。

想象一下,您正在编写一种新的 OO 语言,其访问器方法看起来像实例变量(如 ruby​​ 和 C#)。您可能会从概念语法开始,例如:

  var = expr    // assignment
  method = expr // setter method invocation

但是解析器编译器(甚至不是运行时)会呕吐,因为即使在所有输入都被摸索之后,也无法知道哪种语法是相关的。你面临着哪一个经典的选择。我不能确定细节,但基本上 ruby​​ 是这样做的:

  var = expr    // assignment (new or existing)
  // method = expr, disallow setter method invocation without .

这就是为什么它不模棱两可,而 C# 这样做:

  symbol = expr // push 'symbol=' onto parse tree and decide later
                // if local variable is def'd somewhere in scope: assignment
                // else if a setter is def'd in scope: invocation

对于 C#,“稍后”仍处于编译时。

我确信 ruby​​ 可以做同样的事情,但“稍后”必须在运行时,因为正如本指出的那样,在执行语句之前你不知道哪种情况适用。

我的问题从来没有打算意味着“我真的需要'自我'吗?” 或“正在避免什么潜在的歧义?” 相反,我想知道为什么会做出这个特殊的选择?也许这不是表演。也许它刚刚完成了工作,或者认为最好始终允许 1-liner local 覆盖方法(非常罕见的情况要求)......

但我有点建议,最动态的语言可能是推迟这个决定最长的语言,并根据最上下文信息选择语义:所以如果你没有本地并且你定义了一个 setter,它将使用 setter . 这难道不是我们喜欢 ruby​​、smalltalk、objc 的原因吗,因为方法调用是在运行时决定的,提供了最大的表现力?

4

3 回答 3

85

好吧,我认为出现这种情况的原因qwerty = 4是模棱两可——你是在定义一个新变量,qwerty还是调用 setter?Ruby 通过说它将创建一个新变量来解决这种歧义,因此self.需要。

这是您需要的另一种情况self.

class A
  def test
    4
  end
  def use_variable
    test = 5
    test
  end
  def use_method
    test = 5
    self.test
  end
end
a = A.new
a.use_variable # returns 5
a.use_method   # returns 4

如您所见,访问权限test是不明确的,因此self.是必需的。

此外,这也是为什么 C# 示例实际上不是一个很好的比较的原因,因为您使用 setter 以明确的方式定义变量。如果您在 C# 中定义了一个与访问器同名的变量,则需要使用 来限定对访问器的调用this.,就像 Ruby 的情况一样。

于 2008-09-04T21:04:43.220 回答
18

这里要记住的重要一点是,Ruby 方法可以在任何时候(未)定义,因此为了智能地解决歧义,每次分配都需要运行代码来检查当时是否存在具有分配给名称的方法的任务。

于 2008-09-04T21:28:32.710 回答
18

因为否则就不可能在方法内部设置局部变量。variable = some_value是模棱两可的。例如:

class ExampleClass
  attr_reader :last_set
  def method_missing(name, *args)
    if name.to_s =~ /=$/
      @last_set = args.first
    else
      super
    end
  end

  def some_method
    some_variable = 5 # Set a local variable? Or call method_missing?
    puts some_variable
  end
end

如果self二传手不需要,some_method将 raise NameError: undefined local variable or method 'some_variable'。照原样,该方法按预期工作:

example = ExampleClass.new
example.blah = 'Some text'
example.last_set #=> "Some text"
example.some_method # prints "5"
example.last_set #=> "Some text"
于 2014-05-03T18:02:47.163 回答