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.

Tags: Comparison, Meta-ops, Numeric

May 23, 2010 at 5:15 am |

What the hell? In the case of complex numbers, you base the comparison on the real part, and if they are equal, then you base the comparison on the imaginary part? The usual way to do it is to compare the magnitude of the two numbers.

What you are suggesting produces an ordering, but not useful for the domain where complex numbers are typically used.

Or am I just being completely mislead by my lack of Perl6 comprehension?

May 23, 2010 at 11:52 am |

The notion is to provide an ordering which “fits in”, for the purposes of things like sort. Consider sorting a set of real numbers. This proposal makes the numbers sort the same way no matter whether their representation is Real, Complex, or some combination of the two.

If you did want to sort on their magnitude instead, it is a simple matter of saying

`sort({ .abs })`

— and again, this works the same way whether you have Reals or Complexes.Mind you, the decision to work this way is informed by my sense that people usually do not define an ordering on Complex numbers. (For instance, a quick check suggests the C++ standard does not define any comparison operators for Complex numbers.) If you have more knowledge on this subject I’m certainly interested in hearing it.

This is by no means set in stone yet. For sure one thing I’m still thinking about is whether the list of reals should be automatically extended with zeroes, so that -4 == (-4 + 0i). Yesterday I thought the answer was no, typing this today I’m inclined to think it should be yes.