В Ruby, как действительно принуждает () на самом деле, работают?

Сказано это, когда у нас есть класс Point и знает, как выполнить point * 3 как следующее:

class Point
  def initialize(x,y)
    @x, @y = x, y
  end

  def *(c)
    Point.new(@x * c, @y * c)
  end
end

point = Point.new(1,2)
p point
p point * 3

Вывод:

#<Point:0x336094 @x=1, @y=2>
#<Point:0x335fa4 @x=3, @y=6>

но затем,

3 * point

не понят:

Point не может быть принужден в Fixnum (TypeError)

Таким образом, мы должны далее определить метод экземпляра coerce:

class Point
  def coerce(something)
    [self, something]
  end
end

p 3 * point

Вывод:

#<Point:0x3c45a88 @x=3, @y=6>

Таким образом, это сказано это 3 * point совпадает с 3.*(point). Таким образом, метод экземпляра * берет аргумент point и вызовите на объект 3.

Теперь, начиная с этого метода * не знает, как умножить точку, таким образом,

point.coerce(3)

будет назван и возвратит массив:

[point, 3]

и затем * еще раз применяется к нему, который верен?

Теперь, это понято, и у нас теперь есть новое Point объект, как выполнено методом экземпляра * из Point класс.

Вопрос:

  1. Кто вызывает point.coerce(3)? Это - Ruby автоматически или является этим некоторый код в * метод Fixnum путем ловли исключения? Или это case оператор, что, когда это не будет знать один из известных типов, затем звоните coerce?

  2. Делает coerce всегда должен возвращать массив 2 элементов? Это не может быть никакой массив? Или это может быть массив 3 элементов?

  3. И правило что, исходный оператор (или метод) * будет затем вызван на элемент 0, с аргументом элемента 1? (Элемент 0 и элемент 1 являются этими двумя элементами в том массиве, возвращенном coerce.), Кто делает это? Это сделано Ruby, или это сделано кодом в Fixnum? Если это сделано кодом в Fixnum, затем это - "конвенция", за которой все следуют при выполнении приведения?

    Так мог это быть кодом в * из Fixnum выполнение чего-то вроде этого:

    class Fixnum
      def *(something)
        if (something.is_a? ...)
        else if ...  # other type / class
        else if ...  # other type / class
        else
        # it is not a type / class I know
          array = something.coerce(self)
          return array[0].*(array[1])   # or just return array[0] * array[1]
        end
      end
    end
    
  4. Таким образом, действительно трудно добавить что-то к Fixnumметод экземпляра coerce? Это уже имеет много кода в нем, и мы не можем только добавить несколько строк для улучшения его (но мы будем когда-либо хотеть?)

  5. coerce в Point класс довольно универсален, и он работает с * или + потому что они являются переходными. Что, если это не является переходным, такой, как будто мы определяем Точку минус Fixnum, чтобы быть:

    point = Point.new(100,100)
    point - 20  #=> (80,80)
    20 - point  #=> (-80,-80)
    
60
задан nopole 4 April 2013 в 01:56
поделиться

1 ответ

Короткий ответ: посмотрите, как Matrix это делает.

Идея заключается в том, что coerce возвращает [equivalent_something, equivalent_self], где equivalent_something - это объект, по сути эквивалентный something, но умеющий выполнять операции над вашим классом Point. В либе Matrix мы создаем Matrix::Scalar из любого объекта Numeric, и этот класс умеет выполнять операции над Matrix и Vector.

Чтобы ответить на ваши вопросы:

  1. Да, это непосредственно Ruby (проверьте вызовы rb_num_coerce_bin в источнике), хотя ваши собственные типы тоже должны работать, если вы хотите, чтобы ваш код расширялся другими. Например, если вашей Point#* передан аргумент, который она не распознает, вы попросите этот аргумент coerce превратить себя в Point, вызвав arg.coerce(self).

  2. Да, это должен быть массив из двух элементов, такой, что b_equiv, a_equiv = a.coerce(b)

  3. Да. Ruby делает это для встроенных типов, и вы тоже должны создавать свои собственные типы, если хотите быть расширяемыми:

    def *(arg)
     if (arg не распознан)
     self_equiv, arg_equiv = arg.coerce(self)
     self_equiv * arg_equiv
     end
    end
    
  4. Идея заключается в том, что вы не должны изменять Fixnum#*. Если он не знает, что делать, например, потому что аргумент является Point, то он спросит вас, вызвав Point#coerce.

  5. Переходность (или на самом деле коммутативность) не нужна, потому что оператор всегда вызывается в правильном порядке. Только вызов coerce временно возвращает полученный и аргумент. Не существует встроенного механизма, обеспечивающего коммутативность операторов типа +, == и т.д...

Если кто-то может придумать краткое, точное и ясное описание для улучшения официальной документации, оставьте комментарий!

42
ответ дан 24 November 2019 в 17:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: