Development Tip

Ruby에서 coerce ()는 실제로 어떻게 작동합니까?

yourdevel 2020. 12. 5. 10:47
반응형

Ruby에서 coerce ()는 실제로 어떻게 작동합니까?


수업이 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

이해되지 않음 :

PointFixnum( 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. 그리고 원래 연산자 (또는 메서드) *가 요소 1의 인수와 함께 요소 0에서 호출 된다는 규칙 입니까? (요소 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. coercePoint클래스는 매우 일반적이며 작동 *또는 +그들이 전이 때문이다. Point minus Fixnum을 다음과 같이 정의하는 경우와 같이 전 이적이지 않은 경우에는 어떻게됩니까?

    point = Point.new(100,100)
    point - 20  #=> (80,80)
    20 - point  #=> (-80,-80)
    

짧은 대답 : 어떻게 Matrix하고 있는지 확인 하십시오 .

The idea is that coerce returns [equivalent_something, equivalent_self], where equivalent_something is an object basically equivalent to something but that knows how to do operations on your Point class. In the Matrix lib, we construct a Matrix::Scalar from any Numeric object, and that class knows how to perform operations on Matrix and Vector.

To address your points:

  1. Yes, it is Ruby directly (check calls to rb_num_coerce_bin in the source), although your own types should do too if you want your code to be extensible by others. For example if your Point#* is passed an argument it doesn't recognize, you would ask that argument to coerce itself to a Point by calling arg.coerce(self).

  2. Yes, it has to be an Array of 2 elements, such that b_equiv, a_equiv = a.coerce(b)

  3. Yes. Ruby does it for builtin types, and you should too on your own custom types if you want to be extensible:

    def *(arg)
      if (arg is not recognized)
        self_equiv, arg_equiv = arg.coerce(self)
        self_equiv * arg_equiv
      end
    end
    
  4. The idea is that you shouldn't modify Fixnum#*. If it doesn't know what to do, for example because the argument is a Point, then it will ask you by calling Point#coerce.

  5. Transitivity (or actually commutativity) is not necessary, because the operator is always called in the right order. It's only the call to coerce which temporarily reverts the received and the argument. There is no builtin mechanism that insures commutativity of operators like +, ==, etc...

If someone can come up with a terse, precise and clear description to improve the official documentation, leave a comment!


I find myself often writing code along this pattern when dealing with commutativity:

class Foo
  def initiate(some_state)
     #...
  end
  def /(n)
   # code that handles Foo/n
  end

  def *(n)
    # code that handles Foo * n 
  end

  def coerce(n)
      [ReverseFoo.new(some_state),n]
  end

end

class ReverseFoo < Foo
  def /(n)
    # code that handles n/Foo
  end
  # * commutes, and can be inherited from Foo
end

참고URL : https://stackoverflow.com/questions/2799571/in-ruby-how-does-coerce-actually-work

반응형