Posts Tagged ‘Meta-ops’

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;

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.


Get every new post delivered to your Inbox.