Зачем нужны "Руби". квалификация в классе?

Устройства Ruby, созданные с помощью (c)attr_accessor или вручную, кажутся единственными методами, которым требуется квалификация self. при доступе к самому классу. Похоже, что Ruby оставил мир языков:

  • Для всех методов требуется self/this (например, Perl, и я думаю, что Javascript)
  • Никаких методов не требуется self/this is (С#, Java)
  • Только сеттерам требуется self/this (Ruby?)

Лучшее сравнение - это С# vs Ruby, потому что оба языка поддерживают методы доступа, которые синтаксически работают так же, как переменные экземпляра класса: foo.x = y, y = foo.x. С# называет их свойствами.

Вот простой пример; одна и та же программа в Ruby, затем С#:

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). Теперь С#:

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();
  }
}

Вопрос: Это правда? Есть ли другие случаи, кроме сеттеров, где нужно само? I.e., есть ли другие случаи, когда метод 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

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


@Майк Стоун

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

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

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

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

  • неоднозначный: лексическая двусмысленность (lex должен "смотреть вперед" )
  • Неоднозначность: грамматическая двусмысленность (yacc должен отнестись к анализу синтаксического анализа)
  • AMBIGUOUS: двусмысленность, зная все в момент исполнения.

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

"qwerty = 4" является UNAMBIGUOUS в С# когда нет переменной, определенной...

Я не мог больше согласиться. Но тем не менее, я говорю

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

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

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

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

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

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

Представьте, что вы пишете новый язык OO с помощью методов доступа (например, ruby ​​и С#). Вы, вероятно, начинаете с концептуальные грамматики:

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

Но парсер-компилятор (даже не время выполнения) будет блевать, потому что даже после все входные данные не имеют возможности узнать, какая грамматика уместна. Вы столкнулись с классическим выбором. Я не могу быть уверен в деталях, но в основном рубин делает это:

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

поэтому он нечеткий, а С# делает это:

  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

Для С# "позже" все еще находится во время компиляции.

Я уверен, что Ruby мог бы сделать то же самое, но "позже" должен быть во время выполнения, потому что как указывает Бен, вы не знаете до тех пор, пока не будет выполнено утверждение, в каком случае применяется.

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

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

Ответ 1

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

Ответ 2

Ну, я думаю, причина в том, что это так, потому что 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..

Кроме того, поэтому пример С# на самом деле не является хорошим сопоставлением, потому что вы определяете переменные таким образом, чтобы однозначно использовать средство настройки. Если вы определили переменную в С#, которая была тем же именем, что и accessor, вам нужно будет квалифицировать вызовы к аксессуру с помощью this., как и в случае с Ruby.

Ответ 3

Потому что иначе было бы невозможно установить локальные переменные вообще внутри методов. 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'. As-is, однако, метод работает по назначению:

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