Почему сеттеры Ruby нуждаются в «себе». квалификация в классе? - PullRequest
69 голосов
/ 05 сентября 2008

Ruby-сеттеры - созданные с помощью (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 =(), и он потерпит неудачу (Ruby 1.8.6 в Linux и OS X). Сейчас 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 не может быть вызван без self?

Есть, конечно, много случаев, когда 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

что неоднозначно - это вызов метода или присвоение новой локальной переменной?


@ Майк Стоун

Привет! Я понимаю и ценю ваши замечания и ваши пример был великолепен. Поверь мне, когда я скажу, что если у меня достаточно репутации, Я бы проголосовал за ваш ответ. Тем не менее, мы все еще не согласны:

  • по вопросу семантики и
  • по центральному факту

Сначала я утверждаю, что не без иронии мы ведем семантическую дискуссию о значение слова «двусмысленность».

Когда дело доходит до синтаксического анализа и семантики языка программирования (предмет этого вопроса), конечно, вы бы признали широкий спектр понятий «Двусмысленности». Давайте просто примем несколько случайных обозначений:

  1. неоднозначность: лексическая неоднозначность (лекс должен «смотреть в будущее»)
  2. Неоднозначность: грамматическая неоднозначность (yacc должен отложить до анализа дерева разбора)
  3. AMBIGUOUS: двусмысленность, зная все в момент исполнения

(и между 2-3 тоже есть мусор). Все эти категории разрешены собирать больше контекстной информации, искать все больше и больше в глобальном масштабе. Итак, когда вы говорят,

"qwerty = 4" является НЕПРАВИЛЬНЫМ в C # когда переменная не определена ...

Я не могу не согласиться. Но в то же время я говорю

"qwerty = 4" не однозначно в рубине (как оно существует сейчас)

"qwerty = 4" является неоднозначным в C #

И мы еще не противоречим друг другу. Наконец, вот где мы на самом деле не согласен: любой рубин может или не может быть реализован без дальнейшего языковые конструкции такие, что

Для "qwerty = 4" ruby ​​UNAMBIGOUSLY вызывает существующий сеттер, если
локальная переменная не определена

Вы говорите нет. Я говорю да; мог существовать другой рубин, который ведет себя так же, как ток во всех отношениях, кроме"qwerty = 4" определяет новый переменная, когда нет сеттера и нет локального, он вызывает сеттер, если один существует, и он присваивает локальный, если таковой существует. Я полностью согласен с тем, что я может быть не так. На самом деле, причина, по которой я могу ошибаться, была бы интересна.

Позвольте мне объяснить.

Представьте, что вы пишете новый ОО-язык с методами доступа как экземпляры 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 # 'позже' все еще находится во время компиляции.

Я уверен, что ruby ​​мог бы сделать то же самое, но «позже» должно быть во время выполнения, потому что как указывает Бен, вы не знаете, пока не будет выполнено утверждение, в каком случае применяется.

Мой вопрос никогда не предназначался для обозначения "действительно ли мне нужно" я "?" или что потенциальной двусмысленности избегают? Скорее я хотел знать, почему это было конкретный выбор сделан? Может быть, это не производительность. Может быть, он только что получил работу сделано, или было сочтено наилучшим всегда разрешать 1-локальной строке переопределять метод (довольно редкий случай) ...

Но я вроде бы предполагаю, что наиболее динамичным языком может быть тот, который откладывает это решение дольше всего и выбирает семантику на основе наиболее контекстуального Информация: так что если у вас нет локального и вы определили установщик, он будет использовать установщик. не вот почему нам нравится ruby, smalltalk, objc, потому что вызов метода решается во время выполнения, предлагая максимальную выразительность?

Ответы [ 3 ]

82 голосов
/ 05 сентября 2008

Ну, я думаю, что причина этого в том, что qwerty = 4 неоднозначно - вы определяете новую переменную с именем qwerty или вызываете сеттер? 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 # на самом деле не является хорошим сравнением, потому что вы определяете переменные таким образом, чтобы однозначно использовать установщик. Если бы вы определили переменную в C # с тем же именем, что и метод доступа, вам нужно будет квалифицировать вызовы метода доступа с this., как в случае с Ruby.

18 голосов
/ 05 сентября 2008

Важно помнить, что методы Ruby могут быть (не) определены в любой точке, поэтому для интеллектуального разрешения неоднозначности при каждом назначении необходимо запускать код, чтобы проверить, существует ли метод с назначенным именем. на момент назначения.

16 голосов
/ 03 мая 2014

Потому что иначе было бы невозможно установить локальные переменные внутри методов. 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"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...