мета / динамическое / общее программирование в ruby - PullRequest
0 голосов
/ 23 марта 2011

Я пытаюсь создать универсальный метод:

class Foo
  attr_reader

  def add(object)
    item = @item.find { |item| item.(#the method is calling object.class) == object if item.is_a?(object.class)}
  end
end

Я хочу создать универсальный метод, который сравнивает элемент массива в зависимости от класса параметра.

Пример:

item.method1 == method1 if item.is_a?(method1.class) 

где method1 - это имя класса.

Также мне нужен полный учебник по метапрограммированию и динамическому / универсальному программированию.

1 Ответ

4 голосов
/ 24 марта 2011

Я не уверен в том, что вы пытаетесь сделать из вашего примера. Как правило, в Ruby вы не проверяете типы. Он динамически типизируется по причине: вы можете написать код, который работает для любых объектов, которые поддерживают методы, которые ваш код вызывает для них.

Из того, что я могу сказать из ваших комментариев, вы хотите расширить класс Array, поэтому при вызове метода в массиве, подобном an_array.pack, в массиве выполняется поиск экземпляра Pack и возвращается. В Ruby есть метод, вызываемый всякий раз, когда обнаруживается, что метод не существует, и называется Module#method_missing. Например, если я случайно решу вызвать 4.to_dragon(magic: 4, height: 700), интерпретатор Ruby попытается найти to_dragon как открытый метод, определенный в некотором классе или модуле в цепочке наследования Fixnum (тип чисел). Если вы не сделали что-то странное для этой цепочки, мы получим вызов method_missing для объекта 4 со следующими аргументами: [:to_dragon, { magic: 4, height: 700 }]. По сути, это имя добавляется в начало аргументов, и блок должен быть задан.

Используя эту технику, вы можете переопределить method_missing, чтобы получить этот код как решение:

class String
  def method_to_class
    split('_').map(&:capitalize).join
  end
end

class Array
  def method_missing(method_name, *arguments, &block)
    find do |element|
      element.class.name == method_name.to_s.method_to_class
    end || super
  end
end

Вы добавляете метод в String для преобразования имени метода в имя класса. Затем вы переопределяете method_missing на Array, чтобы проверить каждый элемент, чтобы увидеть, соответствует ли имя класса данному имени класса. Если он найден, он возвращается. В противном случае (и мы делаем это, используя причудливый оператор || Руби), значение, возвращаемое этой функцией, равно nil, и возвращается второй операнд ||. Это является реализацией по умолчанию для method_missing (которую мы получаем по ключевому слову super) и возвращает ошибку, которой заслуживает вызов.

Единственная потенциальная проблема, связанная с этим, заключается в том, что если у вас есть элементы с именами классов, идентичными именам методов, которые уже определены в массивах, то они будут вызываться вместо этого специального метода. Например, вызов an_array.hash даст вам хеш-код массива, а не первый экземпляр хеш-функции.

Более безопасная техника в этом отношении больше похожа на то, что, я думаю, вы пытались сделать. На самом деле он использует объекты класса, и вы можете использовать его для переопределения других методов:

class Array
  def add(class_object)
    class << self
      define_method class_object.name do
        find do |element|
          element.is_a? class_object
        end
      end
    end
  end
end

Это определяет новые методы непосредственно на экземпляре массива. Например:

an_array = [Array, Hash, Dragon].map &:new
an_array.add Hash
an_array.hash    #=> {}

Если в этом ответе нет решения, оно должно как минимум приблизить вас!

...