According to the documentation, sort
compares using infix:<cmp>
.
But:
class Point
{
has Int $.x;
has Int $.y;
method Str { "($!x,$!y)" }
method gist { self.Str }
}
multi sub infix:<cmp>(Point $a, Point $b) { $a.y cmp $b.y || $a.x cmp $b.x }
my @p = Point.new(:3x, :2y), Point.new(:2x, :4y), Point.new(:1x, :1y);
say @p.sort;
gives output:
((1,1) (2,4) (3,2))
When I use:
say @p.sort(&infix:<cmp>);
it does give the proper output:
((1,1) (3,2) (2,4))
Is this a bug, a feature, or a flaw in the documentation?
And is there a way to make .sort()
on a list of a Point
s use a custom sort order without specifying a routine?
I think that's a case of Broken By Design. Consider the following snippet:
my $a = Point.new(:3x, :2y);
my $b = Point.new(:2x, :4y);
say &infix:<cmp>.WHICH;
say $a cmp $b;
{
multi sub infix:<cmp>(Point $a, Point $b) { $a.y cmp $b.y || $a.x cmp $b.x }
say &infix:<cmp>.WHICH;
say $a cmp $b;
}
say &infix:<cmp>.WHICH;
say $a cmp $b;
The definition of the new multi candidate will generate a new proto sub that is only visible lexically. As the sort
method is defined in the setting (conceptionally, an enclosing scope), it won't see your new multi candidate.
It might be possible to make sort
look up &infix:<cmp>
dynamically instead of lexically, though I suspect such a change would have to wait for 6.e even if we decided that's something we want to do, which isn't a given.
As a workaround, you could do something like
constant CMP = &infix:<cmp>;
multi sub infix:<cmp>(Point $a, Point $b) { ... }
BEGIN CMP.wrap(&infix:<cmp>);
for now, though I wouldn't necessarily recommend it (messing with global state considered harmful, and all that jazz)...
The cmp
that is being used is the cmp that is in lexical scope within sort
, not the one you have defined. If you change a few lines to:
multi sub infix:<cmp>(Point $a, Point $b) {
say "Hey $a $b";
$a.y cmp $b.y || $a.x cmp $b.x
}
my @p = Point.new(:3x, :2y), Point.new(:2x, :4y), Point.new(:1x, :1y);
say @p.sort( { $^b cmp $^a } );
Since the cmp
that is being used is the one that is in actual lexical scope, it gets called correctly printing:
Hey (2,4) (3,2)
Hey (1,1) (2,4)
Hey (1,1) (3,2)
((2,4) (3,2) (1,1))
As was required.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With