为什么Ruby setter在课程中需要“自我”资格?

为什么Ruby setter在课程中需要“自我”资格?

Why do Ruby setters need “self.” qualification within the class?

Ruby设置方法(无论是通过(c)attr_accessor还是手动创建的)似乎是在类内部进行访问时唯一需要self.限定的方法。这似乎使Ruby成为语言的世界:

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

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

这是一个简单的例子;在Ruby中使用相同的程序,然后使用C#:

1
2
3
4
5
6
7
8
9
10
11
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#:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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独有的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
  }
}

以相同的方式解决相同的歧义。但是,尽管我很微妙,但我想问的是

  • 已经定义了一种方法,并且
  • 尚未定义局部变量,并且

我们遇到

1
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"外,其他各方面都定义了新的
    如果没有设置器且没有本地变量,则该变量将调用设置器
    存在,如果存在则分配给本地。我完全接受我
    可能是错误的。实际上,我可能错了的原因很有趣。

    让我解释。

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

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

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

    1
    2
      var = expr    // assignment (new or existing)
      // method = expr, disallow setter method invocation without .

    这就是为什么它没有歧义的原因,而C#做到了:

    1
    2
    3
      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.

    这是您需要self.的另一种情况:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    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不明确。例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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'。不过,该方法按预期工作:

    1
    2
    3
    4
    5
    example = ExampleClass.new
    example.blah = 'Some text'
    example.last_set #=>"Some text"
    example.some_method # prints"5"
    example.last_set #=>"Some text"


    推荐阅读