In Ruby, can the coerce() method know what operator it is th

In Ruby, it seems that a lot of coerce() help can be done by

def coerce(something)
  [self, something]
end

that’s is, when

3 + rational

is needed, Fixnum “3” doesn’t know how to handle adding a Rational, so
it asks Rational#coerce for help by calling rational.coerce(3), and this
coerce instance method will tell the caller:

# I know how to handle rational + something, so I will return you

the following:
[self, something]
# so that now you can invoke + on me, and I will deal with Fixnum to
get an answer

So what if most operators can use this method, but not when it is (a -
b) != (b - a) situation? Can coerce() know which operator it is, and
just handle those special cases, while just using the simple [self,
something] to handle all the other cases where (a op b) == (b op a) ?
(op is the operator).

Jian L. wrote:

So what if most operators can use this method, but not when it is (a -
b) != (b - a) situation? Can coerce() know which operator it is, and
just handle those special cases, while just using the simple [self,
something] to handle all the other cases where (a op b) == (b op a) ?
(op is the operator).

In ruby 1.8 and 1.9, != is syntactic sugar for “not ==”. So put the
logic you want in your == method.

class Foo
def ==(other)
puts “Yay!”
false
end
end

puts “Boing!” if Foo.new != Foo.new

prints:

Yay!

Boing!

On 5/10/10, Jian L. [email protected] wrote:

is needed, Fixnum “3” doesn’t know how to handle adding a Rational, so
b) != (b - a) situation? Can coerce() know which operator it is, and
just handle those special cases, while just using the simple [self,
something] to handle all the other cases where (a op b) == (b op a) ?
(op is the operator).

Coerce does not know the operator is being called on behalf of.

Here’s how Rational#coerce is actually implemented. (This is from 1.8.
I think in 1.9 it was rewritten in c):

def coerce(other)
if other.kind_of?(Float)
return other, self.to_f
elsif other.kind_of?(Integer)
return Rational.new!(other, 1), self
else
super
end
end

Generally, you want to return an array containing (a representation
of) other first and then (a representation of) self. That helps ensure
that when the caller calls
other.op self
it won’t end up with the arguments backwards. So operators like - and
/ can work correctly. If you return self first and then other, + and *
will work but, not - and /.

On Tue, May 11, 2010 at 4:46 PM, Caleb C. [email protected]
wrote:
…[for coerce]

Generally, you want to return an array containing (a representation of) other first
and then (a representation of) self.
I’ve been wondering how to express concisely what you’ve concisely
expressed there!
(I’m referring to the “(a representation of)” bit for both other and
self.)

That helps ensure that when the caller calls
other.op self
it won’t end up with the arguments backwards.
So operators like - and / can work correctly.
If you return self first and then other, + and * will work but, not - and /.
With a pedantic former maths student’s hat on:
If you return self first and then other:

  • will (probably?) work because (almost?) all uses of + are
    commutative
    (I’ve never seen a non-commutative use of +, but you never know!);
  • will probably work, but may not if the use of * is not commutative
    (for example matrix multiplication?);
  • and / and ** (probably?) won’t work because (almost?) all uses of
    them
    are non-commutative. (I’ve never seen commutative uses, but …);
    and if <=> is defined for (a representation of) other and
    (a representation of) self, that probably won’t work either!

On 5/12/10, Colin B. [email protected] wrote:

With a pedantic former maths student’s hat on:
If you return self first and then other:

  • will (probably?) work because (almost?) all uses of + are commutative
    (I’ve never seen a non-commutative use of +, but you never know!);
  • will probably work, but may not if the use of * is not commutative
    (for example matrix multiplication?);
  • and / and ** (probably?) won’t work because (almost?) all uses of them
    are non-commutative. (I’ve never seen commutative uses, but …);
    and if <=> is defined for (a representation of) other and
    (a representation of) self, that probably won’t work either!

Ah, thanks I knew there was an appropriate vocab word I was forgetting
and it was commutative. I think your elaboration (above) is a great
deal clearer than mine.

On Wed, May 12, 2010 at 12:16 PM, Colin B.
[email protected] wrote:

it won’t end up with the arguments backwards.
and if <=> is defined for (a representation of) other and
(a representation of) self, that probably won’t work either!

Based on an earlier thread from the OP where the use case was
operations on integers and points, he might be concerned about my
pointing out (no pun intended) that, as far as I know, while there’s a
conventional meaning for multiplying a vector quantity (like a Point)
and a scalar quantity (like an Integer), there isn’t for adding or
subtracting a scalar and a vector.

So we need to allow

1 * Point.new(1,1)

but disallow

1 + Point.new(1, 1)

or

1 - Point.new(1,1)

One way to address this might be something like

class Point
attribute :x, :y

def initialize(x, y)
self.x, self.y = x, y
end

def +(value)
if Point === value
Point.new(x + value.x, y + value.y)
else
raise ArgumentError.new(“Attempt to add a point to a scalar”)
end
end

def -(value)
if Point === value
Point.new(x + value.x, y + value.y)
else
raise ArgumentError.new(“Attempt to subtract a point from a
scalar”)
end
end

def dot_product(other_point)
x*other_point.x + y * other_point.y
end

def (value)
if Point == value
dot_product(value)
else
Point.new(x
value, y*value)
end
end

class ScalarWrapper
attribute :scalar_value

 def initialize(scalar_value)
     self.scalar_value = scalar_value
 end

 def +(value)
    raise ArgumentError.new("Attempt to add scalar to a point")
 end

 def -(value)
    raise ArgumentError.new("Attempt to subtract a scalar from a 

point")
end

  def *(other_point)
     Point.new(scalar_value*other_point.x, 

scalar_value*other_point.x)
end
end
end

def coerce(other)
[Point::ScalarWrapper.new(other), self]
end
end


Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: http://github.com/rubyredrick
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

Hi,
2010/5/13 Colin B. [email protected]:

(B) The actual code in Matrix::Scalar#+ raises an exception for
Numeric + (Matrix or Vector),

but also allows for (Numeric or
Matrix::Scalar) + Matrix::Scalar, with in both cases the result being
Matrix::Scalar, and I can’t immediately think of any circumstances
where those cases would actually arise.

I first thought this would be for performance reason, to coerce once
instead of 2 times, but I don’t see any use case except:
(Matrix::Scalar.new(4) + 3) * matrix
which should never be done, as Matrix::Scalar is supposed to be a
private class:

Private CLASS

class Scalar < Numeric # :nodoc:

Maybe Marc-André (the new maintainer) could give us a hint if he sees
this thread.

Personally, I used a simple Scalar class like in Matrix, but I just
define the * and / operator. Then if you do
Numeric + Matrix, you get:
NoMethodError: undefined method `+’ for #EMatrix::Scalar
Which is almost self-explanatory I think.

Regards,
B.D.

On Wed, May 12, 2010 at 6:13 PM, Rick DeNatale [email protected]
wrote:

Based on an earlier thread from the OP where the use case was
operations on integers and points

  1. the OP might want to take a look at the Vector class in matrix.rb.

  2. In that earlier thread:
    On Mon, May 10, 2010 at 12:49 PM, Rick DeNatale
    [email protected] wrote:

I may be wrong here, it’s early and I haven’t yet had a full cup of coffee
“A mathematician is a machine for turning coffee into theorems”
(* by Alfred Renyi, not Paul Erdos!
http://en.wikiquote.org/wiki/Paul_Erdos *)

but while multiplication of a point (vector) and a scalar makes sense,
I’m not sure that there is a conventional meaning to
subtraction (or addition) of a scalar and a vector.
Yes, I agree: in my draft reply (but left out of my post) was:
“Thought: right or left multiplying (or dividing) a vector (that is
point) by a scalar is OK, but do you really want to allow adding a
vector and a scalar instead of raising an exception?”

  1. Back to this thread!
def +(value)
   raise ArgumentError.new("Attempt to add scalar to a point")
end

I think you’ve anticipated and answered a thought I was about to put
to you in that earlier thread, which was that if we want to:
(a) allow scalar * point,
and (b) disallow scalar - point,
how would you implement that, adding that I could only think of two
ways:

  1. amend code in Fixnum, Bignum, Float, etc, which is messy;
    or 2. use something like the Point::Coerce class in my previous post
    in that thread, but adding code to raise appropriate exceptions.

I then spent quite a bit of time half implementing an example, before
it occurred to me that if there was a Ruby matrix class then that
would probably have to do something similar - there is, and it does:
it’s Matrix::Scalar.

Do you know this class well? I ask because:

(A) I don’t think I’d previously looked at matrix.rb in sufficient
detail for my suggestion of something similar to Matrix::Scalar to be
an unconscious memory of what’s in matrix.rb, in which case that would
be an example in Ruby of there being “only one obvious way to do it”,
and if your code in this thread was similarly starting from first
principles, then that would reinforce this example.

(B) The actual code in Matrix::Scalar#+ raises an exception for
Numeric + (Matrix or Vector), but also allows for (Numeric or
Matrix::Scalar) + Matrix::Scalar, with in both cases the result being
Matrix::Scalar, and I can’t immediately think of any circumstances
where those cases would actually arise. But I may be wrong: the last
cup of coffee I had was nearly 40 years ago, so unless there’s some
sort of homeopathic effect here I think your caffeine stimulation is
likely to be more effective than mine!
A short article in yesterday’s London Metro newspaper cites a recent
study on the effectiveness of caffeine, links:
http://www.businessweek.com/lifestyle/content/healthday/639045.html
http://www.news-medical.net/news/20100512/Caffeine-helps-shift-workers-make-fewer-errors-Cochrane-researchers.aspx

On Thu, May 13, 2010 at 2:07 PM, Benoit D. [email protected]
wrote:


Personally, I used a simple Scalar class like in Matrix, but I just
define the * and / operator. Then if you do
Numeric + Matrix, you get:
NoMethodError: undefined method `+’ for #EMatrix::Scalar
Which is almost self-explanatory I think.
You can drop the “almost”!

Out of curiosity - “I used a simple Scalar class like in Matrix”:
what was that for?
(I did do a quick search on “Benoit D.” and Ruby,
but nothing obviously an answer to that question turned up.)

On 13 May 2010 16:08, Colin B. [email protected] wrote:

On Thu, May 13, 2010 at 2:07 PM, Benoit D. [email protected] wrote:


Personally, I used a simple Scalar class like in Matrix, but I just
define the * and / operator. Then if you do
Numeric + Matrix, you get:
NoMethodError: undefined method `+’ for #EMatrix::Scalar
Which is almost self-explanatory I think.
You can drop the “almost”!
:smiley:

Out of curiosity - “I used a simple Scalar class like in Matrix”:
what was that for?
(I did do a quick search on “Benoit D.” and Ruby,
but nothing obviously an answer to that question turned up.)

:open_mouth: Google seems to not know me so much then …

But, as many Ruby developers, github is the code’s home :wink:
(while you would hardly find my nickname, except maybe from my email)
http://github.com/eregon/math/tree/master/math

(You’ll see I currently didn’t implement Numeric/Matrix, because I
don’t like my Matrix to inverse ‘magically’ in this case)

On 13 May 2010 17:43:24 UTC+2, Colin B. [email protected]
wrote:

I wish I’d known about this
http://github.com/eregon/math/blob/master/math/polynomial.rb
earlier this year: including differentiation and integration!
I was using polynomials to form weighted sums of discrete and
continuous actuarial functions, so I needed integration of the
polynomials.

You’re very welcome to fork and use my code :slight_smile:

I couldn’t find a polynomial.evaluate method, but polynomial.run(n)
seems to give the numerical value of the polynomial for x == n: is
that right?

Yes, #evaluate is bit long for a simple method like that.
Note it’s also aliased to #& for very lazy people like me. So
Polynomial.new(…) & 4

def [email protected](); @coef.map! { |c| -c }; end

q = Polynomial.new( 2, 3, 1 )
p q #=> 2x^2 + 3x + 1
-q
p q #=> - 2x^2 - 3x - 1

If you don’t mind me asking, what was the reason for #- changing the
polynomial to its negative as opposed to returning a new polynomial
which is the negative of the original polynomial?

Thanks for pointing me out, is just some residual from a long time,
when I didn’t know yet about how ruby objects are never duplicated.
I supposed I simplified it later, without seeing this error.
(and I never used -P(x))

So now that’s updated :slight_smile:

I’m also working on symbolic, if you’re interested in this kind of
maths.

Regards,
B.D.

On Thu, May 13, 2010 at 3:25 PM, Benoit D. [email protected]
wrote:

:open_mouth: Google seems to not know me so much then …

But, as many Ruby developers, github is the code’s home :wink:
(while you would hardly find my nickname, except maybe from my email)
http://github.com/eregon/math/tree/master/math
I wish I’d known about this
http://github.com/eregon/math/blob/master/math/polynomial.rb
earlier this year: including differentiation and integration!
I was using polynomials to form weighted sums of discrete and
continuous actuarial functions, so I needed integration of the
polynomials.
I couldn’t find a polynomial.evaluate method, but polynomial.run(n)
seems to give the numerical value of the polynomial for x == n: is
that right?

def [email protected](); @coef.map! { |c| -c }; end

q = Polynomial.new( 2, 3, 1 )
p q #=> 2x^2 + 3x + 1
-q
p q #=> - 2x^2 - 3x - 1

If you don’t mind me asking, what was the reason for #- changing the
polynomial to its negative as opposed to returning a new polynomial
which is the negative of the original polynomial?