Рубі-сетери - створені (c)attr_accessor
чи вручну, - схоже, є єдиними методами, які потребують self.
кваліфікації при доступі до самого класу. Це, здається, ставить Рубі у світ мов:
- Всі методи потребують
self
/this
(як Perl, і я думаю, що Javascript) - Жодні методи не вимагають
self
/this
є (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 не можна використовувати без себе?
Безумовно, є багато випадків, коли самостійність стає необхідною. Це не властиво лише 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
що неоднозначно - це виклик методу чи нове призначення локальної змінної?
@ Майк Стоун
Привіт! Я розумію і ціную зауваження, які ви висловили, і ваш приклад був чудовим. Повірте, коли я скажу, що якщо б у мене було достатньо репутації, я проголосував би вашу відповідь. Проте ми все ще не згодні:
- з питання семантики, і
- за центральним фактом
Спочатку я стверджую, що не без іронії ми маємо семантичну дискусію щодо значення "двозначності".
Коли мова заходить про синтаксичний аналіз та семантику мови програмування (тема цього питання), ви, безсумнівно, визнаєте широкий спектр поняття "двозначність". Давайте просто приймемо випадкові позначення:
- неоднозначний: лексична двозначність (лекс повинен «дивитися вперед»)
- Неоднозначне: граматична двозначність (yacc повинен відкласти аналіз дерева аналізу)
- АМБІГУЙ: двозначність, знаючи все на момент страти
(а також мотлоху між 2-3 теж). Усі ці категорії вирішуються шляхом збору більше контекстної інформації, все більш глобального пошуку. Отже, коли ви говорите:
"qwerty = 4" є НЕМАГБІЗНИМ у C #, коли не визначено змінну ...
Я не міг більше погодитися. Але тим самим я кажу
"qwerty = 4" є недвозначним у рубіні (як зараз існує)
"qwerty = 4" є неоднозначним у C #
І ми ще не суперечимо один одному. Нарешті, тут ми справді не погоджуємось: будь-який ruby міг або не міг бути реалізований без подальших мовних конструкцій, таких
Для "qwerty = 4" ruby UNAMBIGUOUSLY викликає існуючий сеттер, якщо
не визначено локальну змінну
Ви кажете ні. Я кажу так; міг би існувати інший рубін, який поводиться точно так само, як струм, у всіх відношеннях, за винятком того, що "qwerty = 4" визначає нову змінну, коли не існує сеттера та місцевого, він викликає сеттера, якщо такий існує, і присвоює локальному, якщо такий існує. Я цілком визнаю, що можу помилитися. Насправді, причина, з якої я можу помилятися, була б цікавою.
Дозволь пояснити.
Уявіть, що ви пишете нову мову OO за допомогою методів доступу, схожих на екземпляри 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 міг зробити те саме, але "пізніше" повинен був бути під час виконання, тому що, як зазначає Бен, ви не знаєте, доки не буде виконано твердження, який випадок застосовується.
Моє запитання ніколи не означало "чи мені справді потрібне" я "?" або "якої потенційної двозначності уникають?" Швидше я хотів знати, чому був зроблений саме цей вибір? Можливо, це не продуктивність. Можливо, це просто зробило роботу, або вважалося найкращим завжди дозволяти локальному пристрою з 1 лайнером перевизначати метод (досить рідкісна вимога) ...
Але я начебто припускаю, що найбільш динамічною мовою може бути та, яка найдовше відкладає це рішення, і вибирає семантику на основі найбільш контекстуальної інформації: отже, якщо у вас немає локальної мови і ви визначили сеттера, він буде використовувати сетер . Чи не тому ми любимо ruby, smalltalk, objc, оскільки виклик методу вирішується під час виконання, пропонуючи максимальну виразність?
$this->
доступу до змінних екземплярів. Це мене постійно спонукає.