да, отличие примеси от добавления своих слотов в том, что при include создается прокси-класс, который ссылается на модуль (в сам класс ничего не добавляется)
Там сказано, опять же, с человеческой точки зрения (т.е. подмешанные методы будут доступны инстансу класса), однако, там ничего не сказано про техническую реализацию. А техническая реализация (с машинной точки зрения) такая, что, все-таки, создается хидден-прокси-класс, который ссылается на модуль, и который становится super-классом для классам объекта.
Это можно посмотреть на примере (модуль подмешивается, однако, затем метод модуля изменяется и объект использует уже новый метод; в то время, как, если рассматривать ваше предположение, то слоты модуля должны подмешиваться к каждому классу свои и становится независимыми от модуля, но это не так):
irb(main):001:0> module A
irb(main):002:1> def a
irb(main):003:2> puts "A.a"
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> class B; end
=> nil
irb(main):007:0> class C < B
irb(main):008:1> include A
irb(main):009:1> end
=> C
irb(main):010:0> d = C.new
=> #<C:0x81d22f4>
irb(main):011:0> d.a
A.a
=> nil
irb(main):012:0> module A
irb(main):013:1> def a
irb(main):014:2> puts "New A.a"
irb(main):015:2> end
irb(main):016:1> end
=> nil
irb(main):017:0> d.a
New A.a
ага, только это .ancestors выдает «C», и, с человеческой точки зрения, это, действительно, так; с машинной же точки зрения — там создается прокси-класс, который имеет ссылку на «C»
> Такое происходит, когда объявляешь метод для конкретного экземпляра.
Когда объявляешь методы для экземпляра, создает также virtual-класс для объекта, который и хранит методы конкретного инстанса. Таким образом цепь наследования класса следующая: virtual-класс объекта -> класс объекта -> прокси-класс на модуль (может быть несколько) -> super-класс -> и т.д. super-классы.
> А вот при включении модулей методы, кажется, никуда не копируются.
Так методы никуда и не копируются. Создается прокси-класс, который ссылается на модуль. И этот прокси класс становится super-классом для класса объекта.
> при включении (include) модуля в класс, его методы добавляются в класс
если более точно, то методы не добавляются в класс, а создается хидден прокси-класс, который вклинивается в цепь наследования классов, и становится родителем для класса объекта. При этом, если класс «А» уже был отнаследован от какого-нибудь класса (например, «B»), то, повторю, родителем класса «А» становится прокси-класс, а уже родителем прокси-класса — класс «B»
Ключом в хеше может быть все, что угодно, что хешируемо (hashable), т.е. значение ключа получается в результате обработки некой хеш-функции. В Ruby многие объекты хешируемы — символы (атомы; с двоеточием в начале — :a, :symbol, :other_atom и т.д.), строки, числа, и даже массивы и сами хеши.
a = {:a => 10, 'b' => 20} # индекс-символ и индекс-строка
a[1] = 30 # числовой индекс
b = [1, 2, 3]
a[b] = 40 # индекс-массив
c = {:b => 1}
a[c] = 50 # индекс-хеш
a # {"b"=>20, [1, 2, 3]=>40, 1=>30, :a=>10, {:b=>1}=>50}
Обращаться так же:
a[:a], a['b'], a[1], a[b], a[c]
Стоит отметить, что если какое-либо из значений массива b (это и касается хеша-ключа c), который является ключом хеша, изменится, то значение по этому ключу будет nil:
А в PHP нет разницы между «простым» массивом и ассоциативным. В сущности, на уровне реализации это разделение условное. Для ваших целей можете использовать Hash'ы:
Можно еще добавить, что добавление в (пустой) массив по индексу, большему, чем текущая длина, автоматом увеличивает длину массива до указанного индекса, заполняя предварительные значения nil'ами:
> главное — что нам приходится вручную приводить к строке.
Ты говоришь о ручном приведении. Оно имеет место быть всегда при строгой типизации (Ruby, Python в некоторых местах, Java, C++ и т.д.)
> это заставляет программиста отказываться от динамической типизации в пользу явного приведения типов
Да никто никого не заставляет. Еще раз — там, где знаешь, что надо привести — приводи вручную (таких случаев будет меньше, если мыслишь и оперируешь объектами, типизация которых не столь важна в конкретных случаях), если не надо — будет приведено автоматом согласно описанию.
А иначе — пожалуйста — бери любой «статический» язык — он будет и быстрее, и «надежней», и для каждой операции, если повезет, свой оператор может даже будет.
и что? т.е., я правильно понял, — ты отрицаешь даже строгую динамическую типизацию, не то, что динамическую нестрогую? т.е. ты чувствуешь себя спокойно только при статической типизации, которая в почти всегда автоматом строгая?
> можно определять несколько конструкторов, напр. с параметрами и без, с разным числом параметров и пр.
Это что-то из мира С++ и Java. В Ruby второй метод initialize просто-напросто перезапишет первый initialize. В итоге, без параметров Вы не вызовите метод. А параметры по умолчанию можно в самом initialize передавать:
class A
def initialize(a=10)
@a = a
end
end
a = A.new # @a = 10
b = A.new(20) # @a = 20
лишний метод-алиас, поскольку attr_accessor :name создаст геттер (ридер) для @name;
Вообще, свои геттеры (ридеры) и сеттеры (райтеры) лучше описывать, когда установка/чтение проперти более сложное, нежели простое «верни прямое значение», «установи прямое значение». Для примитивных же reader'ов и writter'ов (и для их обобщающих accessor'ов) достаточно соответствующих классовых методов.
def a
#какие-то сложные вычисления возвращающие некоторое значение, а не просто «примитивный „return“
end
def a=
#тоже могут быть сложные вычисление перед установкой проверти, а не просто „примитивный =“
end
attr_reader :b # только для чтения (будет создан метод def b @b end)
attr_writer :c # только для записи (будет создан метод def c=(val) @c = val end)
attr_accessor :d # объединение двух предыдущих „примитивных ридера и райтера“
Это можно посмотреть на примере (модуль подмешивается, однако, затем метод модуля изменяется и объект использует уже новый метод; в то время, как, если рассматривать ваше предположение, то слоты модуля должны подмешиваться к каждому классу свои и становится независимыми от модуля, но это не так):
ага, только это .ancestors выдает «C», и, с человеческой точки зрения, это, действительно, так; с машинной же точки зрения — там создается прокси-класс, который имеет ссылку на «C»
Когда объявляешь методы для экземпляра, создает также virtual-класс для объекта, который и хранит методы конкретного инстанса. Таким образом цепь наследования класса следующая: virtual-класс объекта -> класс объекта -> прокси-класс на модуль (может быть несколько) -> super-класс -> и т.д. super-классы.
> А вот при включении модулей методы, кажется, никуда не копируются.
Так методы никуда и не копируются. Создается прокси-класс, который ссылается на модуль. И этот прокси класс становится super-классом для класса объекта.
если более точно, то методы не добавляются в класс, а создается хидден прокси-класс, который вклинивается в цепь наследования классов, и становится родителем для класса объекта. При этом, если класс «А» уже был отнаследован от какого-нибудь класса (например, «B»), то, повторю, родителем класса «А» становится прокси-класс, а уже родителем прокси-класса — класс «B»
только в случае примитивного аксессора
Конструктор — это allocate (можно его вызвать при создании, без инициализации), а new — всего лишь обертка для allocate + initialize
class A def initialize p self end end a = A.new b = A.allocate # создаст объект, но без init'aнаоборот (вероятно, опечатка) — что сравнивает не только value, но и тип
var'ы, созданные не через eval, удалить нельзя (они все получают внутреннее свойство {DontDelete})
a = {:a => 10, 'b' => 20} # индекс-символ и индекс-строка a[1] = 30 # числовой индекс b = [1, 2, 3] a[b] = 40 # индекс-массив c = {:b => 1} a[c] = 50 # индекс-хеш a # {"b"=>20, [1, 2, 3]=>40, 1=>30, :a=>10, {:b=>1}=>50}Обращаться так же:
Стоит отметить, что если какое-либо из значений массива b (это и касается хеша-ключа c), который является ключом хеша, изменится, то значение по этому ключу будет nil:
В этом случае надо перехешировать хеш:
a.rehash
a[b] # снова работает — 40
obj = { :a => 10, :b => }пардон, увеличивает длину до «указанный индекс + 1», поскольку нумерация с 0
Частично это было показано в «e = Array.new(3) # [nil, nil, nil]».
Так же картина в JavaScript. Однако, Python, например, в таком случае ругается и говорит, что индекса 10 нет.
А статические системы в любом случае быстрее динамических. С другой стороны, статические не обладают такой гибкостью, как динамические.
ну, ествественно, писал просто «на коленке», торопился :)
забрось тогда его :) займись чем-нить «посерьезней» ;)
> где ты поленился привести тип
таких ошибок, где «ты поленился сделать какую-то проверку» может быть и дохрена, не касаемов типизации. Не засчитано.
Ты говоришь о ручном приведении. Оно имеет место быть всегда при строгой типизации (Ruby, Python в некоторых местах, Java, C++ и т.д.)
> это заставляет программиста отказываться от динамической типизации в пользу явного приведения типов
Да никто никого не заставляет. Еще раз — там, где знаешь, что надо привести — приводи вручную (таких случаев будет меньше, если мыслишь и оперируешь объектами, типизация которых не столь важна в конкретных случаях), если не надо — будет приведено автоматом согласно описанию.
А иначе — пожалуйста — бери любой «статический» язык — он будет и быстрее, и «надежней», и для каждой операции, если повезет, свой оператор может даже будет.
Это что-то из мира С++ и Java. В Ruby второй метод initialize просто-напросто перезапишет первый initialize. В итоге, без параметров Вы не вызовите метод. А параметры по умолчанию можно в самом initialize передавать:
class A def initialize(a=10) @a = a end end a = A.new # @a = 10 b = A.new(20) # @a = 20Можно и так:
лишний метод-алиас, поскольку attr_accessor :name создаст геттер (ридер) для @name;
Вообще, свои геттеры (ридеры) и сеттеры (райтеры) лучше описывать, когда установка/чтение проперти более сложное, нежели простое «верни прямое значение», «установи прямое значение». Для примитивных же reader'ов и writter'ов (и для их обобщающих accessor'ов) достаточно соответствующих классовых методов.
def a
#какие-то сложные вычисления возвращающие некоторое значение, а не просто «примитивный „return“
end
def a=
#тоже могут быть сложные вычисление перед установкой проверти, а не просто „примитивный =“
end
attr_reader :b # только для чтения (будет создан метод def b @b end)
attr_writer :c # только для записи (будет создан метод def c=(val) @c = val end)
attr_accessor :d # объединение двух предыдущих „примитивных ридера и райтера“