为什么 Ruby 设置者在类中需要“ self.”资格?

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 上的 Ruby1.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();
}
}

问题: 这是真的吗?除了赛特以外,还有其他需要自我的场合吗?也就是说,Ruby 方法 不能是否在其他情况下被调用 没有自身?

当然有很多情况下,自我 变成了是必要的。这并不是 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

这是模糊的ーー这是一个方法调用还是一个新的局部变量赋值?


@ Mike Stone

你好! 我理解并欣赏你提出的观点 相信我,如果我有足够的名声, 我赞成你的回答,但我们仍然意见不一:

  • 在语义学上
  • 事实的中心点

首先,我要说的是,并非没有讽刺意味,我们正在进行一场语义上的辩论 「歧义」的涵义。

当涉及到解析和形式语义学时(主题 这个问题) ,你肯定会承认一个广泛的概念 ‘歧义’。让我们采用一些随机符号:

  1. 词汇歧义(lex must‘ look ahead’)
  2. 歧义: 语法歧义(yacc 必须遵从解析树分析)
  3. 模棱两可: 在执行的那一刻,模棱两可地知道一切

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

“ qwerty = 4”在 C # 中是不明确的 当没有定义变量时。

我完全同意,但同样的,我要说

“ qwerty = 4”在 Ruby 中是不含糊的 (因为它现在存在)

“ qwerty = 4”在 C # 中是歧义的

我们还没有互相矛盾,最后,这才是我们真正的 不同意: Ruby 可以或不可以在没有任何进一步的 语言结构,

代表“ qwerty = 4”Ruby UNBIGUUSLY 调用现有的 setter
没有定义局部变量

你说不,我说是; 另一个红宝石可能存在,其行为完全相同 在每个方面的电流,除了“ qwerty = 4”定义了一个新的 当没有 setter 和本地存在时,它调用 setter 存在,并且如果存在,它将赋值给局部。我完全接受 可能是错的。事实上,给我一个可能是错的理由会很有趣。

听我解释。

假设您正在编写一种新的 OO 语言,使用访问器方法进行查找 例如 instance vars (例如 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 # ,‘ later’仍然在编译时。

我相信 Ruby 也可以做同样的事情,但是“ later”必须在运行时,因为 正如 Ben 指出的,只有执行了语句,你才能知道是哪种情况 适用。

我的问题从来没有意味着“我真的需要‘自我’吗?”或者“什么。” 避免了潜在的模棱两可?”相反,我想知道这是为什么 特别的选择? 也许不是性能。也许它只是得到了这份工作 否则,最好始终允许1-linerlocal 覆盖 方法(非常罕见的情况下的要求) ..。

但我认为最有活力的语言可能是 推迟这个决定的时间最长,并选择基于最上下文的语义 Info: 所以如果你没有本地的并且你定义了一个 setter,它会使用 setter 这就是为什么我们喜欢 ruby,Smalltalk,objecc,因为方法调用是在运行时决定的, 提供最大的表现力?

10162 次浏览

我认为这种情况的原因是因为 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 案例一样。

这里需要记住的重要一点是,Ruby 方法可以在任何时候被(非)定义,因此为了智能地解决这种模糊性,每次赋值都需要运行代码来检查在赋值时是否有一个具有赋值名称的方法。

因为否则就不可能在方法的所有内部设置局部变量。 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将引发 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"