Posts Tagged ‘Comparison’

Accepting Equality

June 19, 2010

Recently I realized that Numeric and Real need to implement ACCEPTS. As far as I know, the Spec has nothing to say about what this ACCEPTS should do, so I looked at the current implementation. There are two versions of Num.ACCEPTS, one which takes anything and uses == to compare, and one which takes a Complex and checks that the imaginary part is zero, and the real part is equal to the Num. Both ACCEPTS have special cases for NaN, needed because NaN != NaN.

Talking about this with Jonathan, I was initially stumped. If every class had to have an ACCEPTS for its own type, for Real, and for Numeric, things would be a mess. Then it occurred to me: why not implement it by simply calling to ==? As long as == is doing the right thing, ACCEPTS will do the right things as well. (Errr, modulo the NaN issue, which might get tricky.)

Of course, that depended on a notion of “right thing” for ==. Quick check: does a Real number equal the Complex version of the same number?

> 1 == 1 + 0i
0

Huh. Well, let’s get a second opinion:

colomon: alpha: say 1 == 1 + 0i
p6eval: alpha 30e0ed: OUTPUT«1␤»

Whoops. The False answer in current Rakudo is because of my reworking of Numeric comparison. At the time, I was thinking of sorting, and figured it would do no harm if 1 < 1+0i. But of course that looks silly in the context of ==! Apparently the test suite doesn’t have any tests for this case.

As frequently happens with these posts, I started this one without any clear sense of where I should go, but now I’ve got a notion:

1) Fix == so that Reals and equivalent Complexes are equal again.

2) Make a generic ACCEPTS for Numeric that works based on ==.

3) Somehow fold NaN handling into the mix.

Hmmm. Okay, my actual first step is going to be to completely muck up Numeric.ACCEPTS (intentionally), and see what, if any, tests fail, so I can find out the state of testing ACCEPTS.

Thanks to the Perl Foundation and Ian Hague for supporting this work.

Comparing Two Numerics

May 22, 2010

I’ve mentioned before that TimToady and I had ideas for comparing two arbitrary Numeric values. The scheme may not be mathematically ideal, but it does define a handy ordering which works predictably for all Numeric types and is easy to understand.

The idea is that any Numeric value should be able to return a list of real values to use for the purposes of comparison. For a Real, the list’s only element is the value itself. For a Complex, the list has two parts, the real and imaginary values of the Complex, in that order. For your user-defined Numeric type, its whatever list of Reals seems to fit well with the above scheme.

Then, when asked to compare to Numeric values, we generate that list of Reals for each and do a lexicographical compare on the two lists. (We don’t go into an infinite recursion here because comparing two Reals calls different code.)

I’ve been kicking around this notion for a long time now without being able to think of an appropriate name for the method to return a list of Reals. I realized yesterday that the problem was I was thinking of it as being akin to the Real Bridge method — should I call it Bridges, or something like that. But in fact, there’s no need to get Bridge involved at all, we really are just returning a list of Reals. So the perfect name (IMO) is reals.

With that out of the way, I started implementing the code as soon as I had some tuits. Here’s my first working stab at it:

multi sub infix:«<=>»(Numeric $a, Numeric $b) {
    my @a = $a.reals;
    my @b = $b.reals;
    for ^(+@a max +@b) -> $i {
        return -1 if $i >= +@a;
        return +1 if $i >= +@b;
        my $c = @a[$i] <=> @b[$i];
        return $c if $c != 0;
    }
    0;
}

Kind of messy, isn’t it? Then I realized that there was a simple meta-op-based version:

multi sub infix:«<=>»(Numeric $a, Numeric $b) {
    my @a = $a.reals;
    my @b = $b.reals;
    [||] (@a Z<=> @b), (+@a <=> +@b);
}

That right there is one of the major reasons I love Perl 6. Perl 5 had a lovely idiom for chained comparisons, but it required you fix the length of the chain in advance. Perl 6 lets you elegantly extend that idea to handle chains of varying length. (If you’re wondering, the Z comparison does pairwise comparison on the two arrays until one of them runs out, then we tag on one additional -1/0/+1 to indicate which array was shorter, and then run a chained || on the entire lot.

Warning: Neither code example here is fully tested yet! But I was so excited about the meta-op version I had to share.

Thanks to the Perl Foundation and Ian Hague for supporting this work.


Follow

Get every new post delivered to your Inbox.