为啥 Ruby setter 需要“自我”。班级内的资格?

Posted

技术标签:

【中文标题】为啥 Ruby setter 需要“自我”。班级内的资格?【英文标题】:Why do Ruby setters need "self." qualification within the class?为什么 Ruby setter 需要“自我”。班级内的资格? 【发布时间】:2010-09-07 20:47:34 【问题描述】:

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

所有方法都需要self/this(比如Perl,我认为是javascript) 没有方法需要self/this 是(C#、Java) 只有 setter 需要 self/this(Ruby?)

最好的比较是 C# 与 Ruby,因为这两种语言都支持访问器方法,它们在语法上就像类实例变量一样工作:foo.x = yy = 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 的场合吗?即,在其他情况下 不能 调用 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

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


@迈克·斯通

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

关于语义问题,并且 关于事实的中心点

首先我声明,并非没有讽刺意味,我们正在就 “歧义”的含义。

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

    ambiguous:词汇歧义(lex 必须“向前看”) 歧义:语法歧义(yacc 必须遵从解析树分析) 模棱两可:模棱两可在执行的那一刻知道一切

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

"qwerty = 4" 在 C# 中是明确的 当没有定义变量时...

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

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

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

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

对于“qwerty = 4”,红宝石明确 如果存在则调用现有的 setter 没有定义局部变量

你说不。我说是;另一个红宝石可能存在,它的行为完全一样 当前在各个方面,except "qwerty = 4" 定义了一个新的 当不存在 setter 和 local 时的变量,如果存在则调用 setter 存在,如果存在则分配给本地。我完全接受我 可能是错的。事实上,我可能会错的原因会很有趣。

让我解释一下。

假设您正在编写一种新的 OO 语言,其访问器方法看起来 像实例变量(如 ruby​​ 和 C#)。你可能会从 概念语法类似于:

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

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

  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 的原因,因为方法调用是在运行时决定的, 提供最大的表现力?

【问题讨论】:

php 在访问实例变量时也需要$this->。这让我一直很困惑。 只有类方法需要显式接收器,实例方法不需要。 我同意——我也不喜欢这种解决歧义的方法。违反最小意外原则。 @Dogweather Matz 澄清了least surprise 的含义:每个人都有自己的背景。 ...他们可能会对语言的不同方面感到惊讶。然后他们来找我说,“我对这种语言的特性感到惊讶,因此 Ruby 违反了最小惊讶原则。”等待。等待。最小意外原则不仅适用于您。最少意外原则是指最少my 意外的原则。也就是学好 Ruby 之后的最小意外原则。 【参考方案1】:

因为否则根本不可能在方法内部设置局部变量。 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

如果二传手不需要selfsome_method 将引发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"

【讨论】:

哈哈。我正要赞成这个答案,然后意识到这是我一年前的。 ;-) 我认为这是唯一清楚说明为什么你不能没有 self. 的答案。 我应该补充一点,Java 和它的同类能侥幸逃脱的原因是因为作为静态类型语言,编译器可以提前确定是否会发生冲突,并引发相应的错误。动态(脚本语言)、弱类型(php)和鸭子类型(ruby/python)语言没有这样的奢侈,唉【参考方案2】:

好吧,我认为出现这种情况的原因是因为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 的情况一样。

【讨论】:

【参考方案3】:

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

【讨论】:

这是不正确的,真正的原因是简单地声明x=5 会实例化一个局部变量x,它会覆盖任何现有的setter self.x=。为了解决这个歧义,如果你想调用 setter x= 你需要通过声明 self.x= 来明确说明你想要做什么

以上是关于为啥 Ruby setter 需要“自我”。班级内的资格?的主要内容,如果未能解决你的问题,请参考以下文章

为啥自我实现的 getter 应该保留并自动释放返回的对象?

封装 - 为啥我使用 getter setter 将我的数据成员公开,如果我已经在课堂上声明它们是私有的

当我们可以用 setter 做同样的事情时,为啥我们需要使用 builder 设计模式? [复制]

为啥在 CDI 中使用构造函数而不是 setter 注入?

为啥 Swift 不像 Java 或 C# 那样对属性使用 getter 和 setter?

ruby ABC班级/排名整体