kobame kobame - 4 months ago 11
Perl Question

perl List::AllUtils uniq & sort

Trying this:

perl -Mv5.14 -MList::AllUtils=uniq -E 'my(@x) = qw(2 3 1 2); say "@x"; my(@y) = uniq @x; say "@y"'


It correctly prints

2 3 1 2
2 3 1


Now want the sorted output, so tried:

perl -Mv5.14 -MList::AllUtils=uniq -E 'my(@x) = qw(2 3 1 2); say "@x"; my(@y) = sort uniq @x; say "@y"'


Surprisingly it prints:

2 3 1 2
2 1 3 2


Switching the order of the
uniq
and
sort


perl -Mv5.14 -MList::AllUtils=uniq -E 'my(@x) = qw(2 3 1 2); say "@x"; my(@y) = uniq sort @x; say "@y"'


gives the correct result

2 3 1 2
1 2 3


So, compared them using the
MO=Deparse
.

The 1st:

perl -MO=Deparse -Mv5.14 -MList::AllUtils=uniq -E 'my(@x) = qw(2 3 1 2); say "@x"; my(@y) = uniq @x; say "@y"'
sub BEGIN {
require v5.14;
}
use List::AllUtils (split(/,/u, 'uniq', 0));
use strict;
use feature 'current_sub', 'evalbytes', 'fc', 'postderef_qq', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval';
my(@x) = ('2', '3', '1', '2');
say join($", @x);
my(@y) = &uniq(@x);
say join($", @y);
-e syntax OK


the second:

perl -MO=Deparse -Mv5.14 -MList::AllUtils=uniq -E 'my(@x) = qw(2 3 1 2); say "@x"; my(@y) = sort uniq @x; say "@y"'
sub BEGIN {
require v5.14;
}
use List::AllUtils (split(/,/u, 'uniq', 0));
use strict;
use feature 'current_sub', 'evalbytes', 'fc', 'postderef_qq', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval';
my(@x) = ('2', '3', '1', '2');
say join($", @x);
my(@y) = (sort uniq @x);
say join($", @y);
-e syntax OK


third:

perl -MO=Deparse -Mv5.14 -MList::AllUtils=uniq -E 'my(@x) = qw(2 3 1 2); say "@x"; my(@y) = uniq sort @x; say "@y"'
sub BEGIN {
require v5.14;
}
use List::AllUtils (split(/,/u, 'uniq', 0));
use strict;
use feature 'current_sub', 'evalbytes', 'fc', 'postderef_qq', 'say', 'state', 'switch', 'unicode_strings', 'unicode_eval';
my(@x) = ('2', '3', '1', '2');
say join($", @x);
my(@y) = &uniq(sort(@x));
say join($", @y);
-e syntax OK


The difference is how the
uniq
subroutine is called:

my(@y) = (sort uniq @x); # sort uniq @x
my(@y) = &uniq(sort(@x)); # uniq sort @x


I understand than the
uniq
is an subroutine provided by the
List::AllUtils
and the
sort
is an builtin function, but using the
uniq
as:

my(@y) = &uniq(sort(@x));


doesn't seems for me very intutive.

Must I use it in the
&uniq(...)
form, e.g. with
&
and parenthesis? Coudl someone please add some more info about?

Answer

If you run it with use strict and use warnings it tells you what is wrong.

use strict;
use warnings;
use feature 'say';
use List::AllUtils 'uniq';

my (@x) = qw(2 3 1 2);
say "@x";
my (@y) = sort uniq @x;
say "@y";

This gives the warning Sort subroutine didn't return single value at. sort thinks that uniq is the subroutine that it should use to sort the list.

The sort documentation explains this.

sort SUBNAME LIST
sort BLOCK LIST
sort LIST

You can also give it a sub name. It's a bit counter-intuitive to use that subname directy (i.e. not as a string, or as a code reference), but that's how it is supposed to be. There even is an example in the docs.

# sort using explicit subroutine name
sub byage {
    $age{$a} <=> $age{$b};  # presuming numeric
}
my @sortedclass = sort byage @class;

You can make sort ignore the sub name and use the return value instead by prepending a + sign.

my (@x) = qw(2 3 1 2);
say "@x";
my (@y) = sort +uniq @x;
say "@y";

__END__
2 3 1 2
1 2 3

And there is no warning now. sort +uniq(@x) also works, and is easier to read.

Comments