Короткий:
Есть ли способ создать расширенный класс Array с именем A, который поддерживает полную цепочку методов с map, sort, sort_by и новым методом word и не вредит классу Array?
Пример:
[
A.new(a).word,
A.new(a).sort.word,
A.new(a).sort_by(&:-@).word,
A.new(a).map(&:chr).sort.map(&:ord).word
]
Длинная история:
Я решил эту ката и создал код, который расширяет класс Array новым методом word:
class Array
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
end
def sort_transform(a)
[
a.word,
a.sort.word,
a.sort_by(&:-@).word,
a.map(&:chr).sort.map(&:ord).word
].join ?-
end
Тогда я подумал, что не стоит добавлять метод для таких базовых классов. И я попытался реализовать новый класс, который унаследовал бы все поведение от Array.
class A < Array
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
end
Это дополнение ломает мой код, потому что map, sort, sort_by возвращает экземпляр Array: A.new([1,2,3]).sort.class # Array. И Array не понимает метод word. И вместо A.new(a).sort.word я должен инкапсулировать часть цепочки в конструктор A.new: A.new(a.sort).word. Это определенно ломает цепочку методов чистый.
Можно ли расширить Array таким образом, чтобы достичь таких чистых цепочек методов; A.new(a).sort.word?
Когда я попытался написать строку: класс А <массив
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
def sort
A.new(self.sort)
end
end
Это приносит мне main.rb:8:in 'sort': stack level too deep (SystemStackError)
Наконец, уже написав эти строки, я нашел способ избежать дипстека: преобразовать себя в Array, а затем снова преобразовать в A.
class A < Array
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
def sort
A.new(self.to_a.sort)
end
end
Так это единственный способ реализовать такое расширение?
Вместо этого я бы использовал уточнение Array. Почему никто не использует уточнения?
@ Амадан, вот почему я написал этот вопрос :) Чтобы получить информацию, которую вы дали.
Зачем вам добавлять метод экземпляра в Array, подкласс Array или уточнять Array. Почему бы просто не определить метод word с одним аргументом для данного массива?
@CarySwoveland: Почему бы вам не уточнить? Вот почему они для. Зачем уточнять функцию? Потому что это связывает лучше, и связывание читается более естественно, поскольку оно линейно представляет причинно-следственную связь. f.each_line.map(&:length).max есть поток. find_max(get_lines(f).map(&:length)) ходит туда-сюда и несет более высокую умственную нагрузку. Непараллелизм между синтаксисом и порядком выполнения — одна из причин, почему я не люблю Python: почему len функция, а lower метод, но map функция, но join метод на нить, но sorted функция, но sort метод... И понимание... >.<
@ Амадан, твой аргумент убедителен. Хороший ответ!



Если вы когда-нибудь захотите исправить базовый класс, но считаете, что это некрасиво, вы должны помнить, что уточнения сделаны только для этого сценария.
module ArrayWithWord
refine Array do
def word
[*self[0..1],*self[-2..-1]].map(&:chr).join
end
end
end
class WordTest
using ArrayWithWord
# [].word works here
def self.sort_transform(a)
[
a.word,
a.sort.word,
a.sort_by(&:-@).word,
a.map(&:chr).sort.map(&:ord).word
].join ?-
end
end
WordTest.sort_transform(%w(foo bar baz quux))
# => "fbbq-bbfq-bbfq-bbfq"
# [].word doesn't work here
[].word
# => NoMethodError (undefined method `word' for []:Array)
Статья была передана мне words.steveklabnik.com/beware-subclassing-ruby-core-classes И разработчик, который дал мне эту статью, сказал, что экземпляры возвращаются к
Arrayв цепочке методов вRubyиз-заCкода. Он реализует для скорости.