Копирование / наследование класса Ruby Matrix (core / std lib)

#ruby #inheritance #matrix #singleton

#ruby #наследование #матрица #синглтон

Вопрос:

Я попытался расширить существующий одноэлементный класс в Ruby, в качестве примера класс Matrix.

Моим первым быстрым и грязным решением было исправление monkey (повторно открыть класс и расширить функциональность).

Но я думаю, что обезьянье исправление в целом нехорошо, особенно если кто-то пытается перезаписать базовые методы основных классов, такие как String, Integer, …

Следующим шагом было выяснить, как я могу получить реальную печатную копию класса Matrix с новым именем (например, MatrixExt), который ведет себя как отдельный синглтон.

 MatrixExt = Matrix
  

не выполнила задание, поскольку это приводит к:

 MatrixExt.scalar(2,0)
=> Matrix[[0, 0], [0, 0]]
  

Таким образом, я получаю только несколько имен для одного и того же синглтона. Не то, что я хочу.

Тот же результат для clone и dup методов.

Также наследование класса не будет работать:

 class MatrixExt < Matrix
  # patches ...
end

MatrixExt.scalar(2,0)
=> Matrix[[0, 0], [0, 0]]
  

И это была самая запутанная часть, потому что в самостоятельно определенных классах возможно получить унаследованный класс. (Итак, почему классы core / std lib работают по-разному?)

Мое текущее решение состоит в том, чтобы иметь модуль с расширением, а затем явно использовать .extend после инициализации, например:

 m = Matrix.scalar(2,0).extend(MatrixExtModule)
  

На данный момент это нормально, но мой вопрос:

Есть ли другое решение и — если да — как это сделать?

(Нет, копирование matrix.rb, конечно, не лучший способ. ; o)

Что я делаю не так или где я думаю неправильно?

Заранее спасибо за любое решение и / или пищу для размышлений!

Ответ №1:

Это ошибка.

Я создал проблему на redmine.ruby-lang.org , что рекомендуется сделать, чтобы исправить эти ошибки.

Я исправил библиотеку, но, боюсь, она не будет доступна до версии Ruby 1.9.4.

Комментарии:

1. И я думал, что это было запланированное поведение. ; o)

Ответ №2:

Если вы посмотрите на то, как Matrix это реализовано, вы заметите, что все методы, такие как scalar , diagonal и т.д., Вызывают частный new метод, который всегда будет возвращать новый Matrix объект (вы не переопределяете методы, поэтому Ruby обратится к реализации суперкласса, где new является self неявным получателем Matrix , а именно, к ,, классу).

Я думаю, вам лучше всего обернуть все ваши исправления в модуль и обезьяний патч Matrix таким образом.

Комментарии:

1. Вы имеете в виду что-то вроде: class Matrix; extend MatrixExt; end

2. (Имеется в виду: ... include MatrixExt ... )

3. Если бы вы это сделали, class MyObject; end; MyObject.new в конечном итоге вы получили бы объект с классом MyObject , а не Object MyObject.new , даже если вы не внедрили, что это не так,,,. Почему это отличается?

4. Если вы посмотрите на исходный код C, Class#new вы увидите obj = rb_obj_alloc(klass); , что означает, что возвращаемый объект будет создан с текущим классом, прежде чем initialize будет вызван с помощью rb_obj_call_init(obj, argc, argv); . В Matrix примере new приведен частный метод, который не может быть вызван с явным получателем, поэтому self всегда будет, Matrix когда другие методы, подобные diagonal , вызывают его.