Ruby setters - 无论是由类创建(c)attr_accessor
还是手动创建- 似乎是self.
在类本身内访问时需要限定的唯一方法.这似乎使Ruby独自成为语言世界:
所有方法都需要self
/ this
(像Perl,我认为是Javascript)
没有方法需要self
/ this
是(C#,Java)
只有setter需要self
/ this
(Ruby?)
最好的比较是C#VS红宝石,因为这两种语言都支持这句法工作就像类的实例变量的访问方法: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(); } }
问题:这是真的吗?除了需要自我的主持人之外还有其他场合吗?也就是说,在没有自我的情况下,有没有其他情况下无法调用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
嗨!我理解并欣赏你所提出的观点,你的榜样很棒.相信我,当我说,如果我有足够的声誉,我会投票你的回应.但我们仍然不同意:
在语义问题上,和
在一个中心点上
首先,我声称,不是没有讽刺意味,我们正在就"歧义"的含义进行语义辩论.
当谈到解析和编程语言语义(这个问题的主题)时,你肯定会承认广泛的概念"歧义".我们只是采用一些随机表示法:
含糊不清:词汇含糊不清(lex必须'向前看')
不明确:语法模糊(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(如果存在),并且如果存在则分配给本地.我完全接受我可能是错的.事实上,我可能错的原因很有趣.
让我解释.
想象一下,你正在编写一个新的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#,'later'仍处于编译时.
我确信ruby也可以这样做,但是'later'必须在运行时,因为ben指出你不知道,直到语句执行哪种情况适用.
我的问题从来没有打算表示"我真的需要'自我'吗?" 或"正避免什么潜在的歧义?" 相反,我想知道为什么这个特别的选择?也许这不是表现.也许它只是完成了工作,或者最好总是允许1-liner本地覆盖方法(一种非常罕见的情况要求)......
但我有点建议最动态的语言可能是推迟这个决定最长的语言,并根据最大的上下文信息选择语义:所以如果你没有本地并且你定义了一个setter,它会使用setter .这不是我们喜欢ruby,smalltalk,objc的原因,因为方法调用是在运行时决定的,提供最大的表现力吗?
好吧,我认为这种情况的原因是因为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方法可以在任何时候(un)定义,因此为了智能地解决歧义,每个赋值都需要运行代码来检查当时是否存在具有赋值名称的方法任务.
因为否则就不可能在方法中设置局部变量.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"