Ruby setter——无论是由其创建还是手动创建——似乎是在类本身中访问时(c)attr_accessor
唯一需要限定的方法。self.
这似乎使 Ruby 在语言世界中独树一帜:
- 所有方法都需要
self
/this
(比如 Perl,我认为是 Javascript) - 没有方法需要
self
/this
is (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
这是模棱两可的——这是一个方法调用还是一个新的局部变量赋值?
@迈克·斯通
你好!我理解并欣赏你提出的观点,你的例子很棒。相信我,如果我有足够的声誉,我会投票赞成你的回应。然而我们仍然不同意:
- 在语义问题上,以及
- 在一个事实的中心点上
首先,我声称,并非没有讽刺意味,我们正在就“歧义”的含义进行语义辩论。
当谈到解析和编程语言语义(这个问题的主题)时,你肯定会承认“歧义”这个概念的广泛范围。让我们采用一些随机符号:
- ambiguous:词汇歧义(lex must 'look ahead')
- 歧义:语法歧义(yacc 必须遵循解析树分析)
- 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 的原因吗,因为方法调用是在运行时决定的,提供了最大的表现力?