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).
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.
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 5/10/10, Jian Lin <blueskybreeze@gmail.com> wrote:
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).
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 Tue, May 11, 2010 at 4:46 PM, Caleb Clausen <vikkous@gmail.com> wrote:
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
···
On Wed, May 12, 2010 at 12:16 PM, Colin Bartlett <colinb2r@googlemail.com> wrote:
On Tue, May 11, 2010 at 4:46 PM, Caleb Clausen <vikkous@gmail.com> 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!
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 5/12/10, Colin Bartlett <colinb2r@googlemail.com> 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!
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:
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! Paul Erdős - Wikiquote *)
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?"
3. Back to this thread!
[if] 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
...
def +(value)
# add if value is point, raise exception if it isn't
...
class ScalarWrapper
attribute :scalar_value
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
···
On Wed, May 12, 2010 at 6:13 PM, Rick DeNatale <rick.denatale@gmail.com> wrote:
On Mon, May 10, 2010 at 12:49 PM, Rick DeNatale <rick.denatale@gmail.com> wrote:
(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.
···
2010/5/13 Colin Bartlett <colinb2r@googlemail.com>:
Out of curiosity - "I used a simple Scalar class like in Matrix":
what was that for?
(I did do a quick search on "Benoit Daloze" and Ruby,
but nothing obviously an answer to that question turned up.)
···
On Thu, May 13, 2010 at 2:07 PM, Benoit Daloze <eregontp@gmail.com> 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.
...
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 Daloze" and Ruby,
but nothing obviously an answer to that question turned up.)
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?
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?
···
On Thu, May 13, 2010 at 3:25 PM, Benoit Daloze <eregontp@gmail.com> 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
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
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
I'm also working on symbolic, if you're interested in this kind of maths.
Regards,
B.D.
···
On 13 May 2010 17:43:24 UTC+2, Colin Bartlett <colinb2r@googlemail.com> wrote: