Установщики 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();
}
}
Вопрос: Это правда? Существуют ли другие случаи, помимо сеттеров, когда требуется self? То есть, есть ли другие случаи, когда 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
что неоднозначно - это вызов метода или назначение новой локальной переменной?
@Mike Stone
Здравствуй! Я понимаю и ценю высказанные вами замечания и ваши пример был отличным. Поверьте мне, когда я говорю, если бы у меня было достаточно репутации, Я проголосую за ваш ответ. Тем не менее, мы все еще не согласны:
Сначала я утверждаю, не без иронии, у нас ведутся семантические дискуссии о значение слова «двусмысленность».
Что касается синтаксического анализа и семантики языков программирования (предмет этого вопроса), вы, конечно, допускаете широкий спектр понятия «двусмысленность». Давайте просто воспользуемся случайными обозначениями:
(и барахло между 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#
И мы пока не противоречим друг другу. Наконец, вот где мы действительно не согласен: Ruby может быть или не может быть реализован без дальнейшего языковые конструкции такие, что,
For "qwerty = 4," ruby UNAMBIGUOUSLY invokes an existing setter if there
is no local variable defined
Вы говорите нет. Я говорю да; может существовать другой рубин, который ведет себя точно так же, как текущий во всех отношениях, Кроме "qwerty = 4" определяет новый переменная, когда нет сеттера и локального не существует, она вызывает сеттер, если один существует, и он назначается локальному, если он существует. Я полностью согласен с тем, что я мог ошибаться. На самом деле, было бы интересно узнать причину, по которой я мог ошибаться.
Позволь мне объяснить.
Представьте, что вы пишете новый объектно-ориентированный язык с методами доступа, которые выглядят как экземпляры vars (например, 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 мог бы сделать то же самое, но «позже» должно было быть во время выполнения, потому что как указывает Бен, вы не знаете, пока оператор не будет выполнен, в каком случае применяется.
Мой вопрос никогда не означал «действительно ли мне нужно« я »?» или что потенциальная двусмысленность избегается? "Скорее я хотел знать, почему это конкретный выбор сделан? Может дело не в производительности. Может быть, он просто получил работу сделано, или было сочтено лучшим всегда позволять однострочному локальному переопределить метод (требование довольно редкого случая) ...
Но я вроде как предполагаю, что наиболее динамичным языком может быть тот, который откладывает это решение дольше всех и выбирает семантику на основе наиболее контекстных информация: поэтому, если у вас нет локального и вы определили сеттер, он будет использовать сеттер. Не вот почему нам нравятся ruby, smalltalk, objc, потому что вызов метода решается во время выполнения, предлагая максимальную выразительность?
Явный получатель необходим только для методов класса, но не для методов экземпляра.
Согласен - мне тоже не нравится такой способ разрешения разногласий. Нарушает принцип наименьшего удивления.
@Dogweather Matz поясняет, что он имеет в виду под наименьший сюрприз: У каждого есть индивидуальное прошлое. ... они могут быть удивлены разными аспектами языка. Затем они подходят ко мне и говорят: «Меня удивила эта особенность языка, поэтому Ruby нарушает принцип наименьшего удивления». Ждать. Ждать. Принцип наименьшего удивления подходит не только вам. Принцип наименьшего удивления означает принцип наименьшего удивления мой. И это означает принцип наименьшего удивления после того, как вы хорошо изучите Ruby.

Что ж, я думаю, причина этого в том, что 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.
Здесь важно помнить, что методы Ruby могут быть (не) определены в любой момент, поэтому для разумного разрешения неоднозначности при каждом назначении потребуется запускать код, чтобы проверить, существует ли в данный момент метод с назначенным именем. присвоения.
Это неверно, настоящая причина в том, что простое указание x=5 создает экземпляр локальной переменной x, которая переопределяет любой существующий установщик self.x=. Чтобы устранить эту двусмысленность, если вы хотите вызвать сеттер x=, вам нужно явно указать, что вы хотите сделать, указав self.x=
Потому что иначе вообще невозможно было бы установить локальные переменные внутри методов. 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"
Ха-ха. Я собирался проголосовать за этот ответ, но понял, что это был мой ответ год назад. ;-)
Я думаю, что это единственный ответ, который ясно указывает, почему вы никогда не сможете обойтись без self..
Я должен добавить к этому, причина, по которой Java и подобные ей сходит с рук, заключается в том, что в качестве статически типизированных языков компилятор может заранее определить, будет ли конфликт, и соответственно вызвать соответствующую ошибку. У динамических (языки сценариев), языков со слабой типизацией (php) и утиной типизации (ruby / python) такой роскоши, увы, нет.
PHP также требует
$this->при доступе к переменным экземпляров. Это все время сбивает меня с толку.