为什么Ruby setter需要"自我"。班内资历?

时间:2020-03-05 18:47:53  来源:igfitidea点击:

Ruby setters是由(c)attr_accessor创建的,还是手动创建的,是在类本身内部进行访问时唯一需要具备self。资格的方法。这似乎使Ruby成为语言的世界:

  • 所有方法都需要" self" /" this"(例如Perl,我认为是Javascript)
  • 没有方法要求self/this是(C#,Java)
  • 只有设置员需要self/this(Ruby?)

最好的比较是Cvs Ruby,因为两种语言都支持访问器方法,这些访问器方法在语法上类似于类实例变量:foo.x = y和y = foo.x。调用它们的属性。

这是一个简单的例子;在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();
  }
}

问题:这是真的吗?除了二传手以外,还有其他需要自我的场合吗?即,在其他情况下,没有self不能调用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

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

@迈克·斯通

你好!我了解并感谢我们提出的观点以及
例子很棒。当我说的时候相信我,如果我有足够的声誉,
我会投票支持你的答复。但是我们仍然不同意:

  • 关于语义的问题,以及
  • 以事实为中心

首先,我要说,并非没有讽刺意味,我们正在就
"歧义"的意思。

在解析和编程语言语义时(主题
问题),我们肯定会接受广泛的概念
"模棱两可"。让我们采用一些随机符号:

  • 模棱两可:词汇歧义(lex必须"向前看")
  • 歧义:语法歧义(yacc必须遵从解析树分析)
  • 歧义:执行时一无所知

(还有2-3之间的垃圾)。所有这些类别均通过以下方式解决
收集更多的上下文信息,在全球范围内寻找越来越多的信息。所以当你
说,

"qwerty = 4" is UNAMBIGUOUS in C#
  when there is no variable defined...

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

"qwerty = 4" is un-Ambiguous in ruby
  (as it now exists)
  
  "qwerty = 4" is Ambiguous in C#

而且我们还没有相互矛盾。最后,这是我们真正的地方
不同意:没有任何进一步的措施,红宝石将无法实施
语言构造,

For "qwerty = 4," ruby UNAMBIGUOUSLY
  invokes an existing setter if there

  is no local variable defined

你说不我说是;可能存在另一个红宝石,其行为与
当前,除" qwerty = 4"外,其他各方面都定义了新的
当没有setter且本地不存在时的变量,如果有
存在,如果存在则分配给本地。我完全接受我
可能是错的。实际上,我可能错了的原因很有趣。

让我解释。

想象一下,我们正在编写一种具有访问器方法的新OO语言。
像实例变量(如ruby和C#)。你可能会开始
概念语法如下:

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

但是解析器编译器(甚至不是运行时)也会呕吐,因为即使
所有的输入内容都是古怪的,无法知道哪种语法是相关的。
我们将面对哪个经典选择。我不确定细节,但是
基本上红宝石这样做:

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可以做到这一点,但是"以后"必须在运行时进行,因为
正如ben指出的那样,在执行该语句之前我们不知道
适用。

我的问题从未打算表示"我真的需要'自我'吗?"。要不然是啥
避免了潜在的歧义?"相反,我想知道为什么会这样
做出的特别选择?也许不是性能。也许刚找到工作
完成,或者最好始终允许1-liner local覆盖
方法(非常少见的案例要求)...

但我建议,最具活力的语言可能是
推迟此决定最长的时间,并根据最相关的上下文选择语义
信息:因此,如果我们没有本地语言,并且定义了一个setter,它将使用该setter。是不是
这就是为什么我们喜欢ruby,smalltalk,objc,因为方法调用是在运行时决定的,
提供最大的表现力?

解决方案

回答

好吧,我认为是这样的原因是因为qwerty = 4是模棱两可的,是我们要定义一个名为qwerty的新变量还是调用setter吗? Ruby通过说会创建一个新变量来解决这种歧义,因此需要" 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。"是必需的。

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

回答

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