"isn't numeric" error in "sort" after "uniq"
Asked Answered
N

1

9
use List::MoreUtils 'uniq';
print join ", ", sort uniq ("b", "a", "a");

results in Argument "a" isn't numeric in sort at ...

print join ", ", uniq sort ("b", "a", "a");

works as expected.

print join ", ", sort {$a cmp $b} uniq ("b", "a", "a");

works too - but what is the problem with the first example?

Nye answered 10/8, 2019 at 18:13 Comment(6)
My guess is that the sort uniq ... is interpreted as the sort SUBNAME LIST syntax where SUBNAME is uniq instead of the intended sort LIST syntax. You can achieve the latter using sort @{[uniq ("b", "a", "a")]}Incestuous
@HåkonHægland Why isn't sort (uniq @list) parsed as sort LIST syntax when sort ("a","b","c") is? Is this just a strange quirk of the Perl parser?Citrange
@JimGarrison In sort (uniq @ary) there is no reason for the interpreter to see that uniq is anything other than a bareword in that list (and is thus out of place). Thus sort (uniq(@ary)); worksBixler
@Jim Garrison, sort's syntax is what's quirky. Specifically, it's very ambigous, so Perl needs to perform all kinds of guesses.Fascicle
Here's something insane: while sort(uniq(@ary)) works, sort(uniq (@ary)) does not. The only difference is the space I added between uniq and (@ary). I always thought whitespace was not semantically significant!Ingeminate
@Ingeminate It's the extra parens that don't count so (word (@ary)) is no different from (word @ary). It's a good point though, that li'l space does count here (under sort)Bixler
B
7

That code falls under the sort invocation of

sort SUBNAME LIST

...
If SUBNAME is specified, it gives the name of a subroutine that returns an integer less than, equal to, or greater than 0 , depending on how the elements of the list are to be ordered.

The uniq in the first example is taken as a bareword that specifies the name of the sub to use for sorting and qw(b a a) is the list to sort -- you aren't uniq-ing the list (so to speak) but are using uniq as a sorting function for that list.

The error message comes as a sorting function needs to return a number and uniq returns strings.

You've discovered one way to make it work, and can also use the unary +

say for sort +uniq(@ary);    # or: say for sort + uniq @ary;

since what follows + is treated as an expression, so sort gets the evaluated list. See this post and this post for discussion of such uses of the unary +.

Or use parens through and through

say for sort (uniq(@ary));

Here the inner pair is also necessary since with sort (uniq @ary) the uniq is interpreted as a bareword in that list, thus out of place. Note that sort (uniq (@ary)) won't work since extra parens don't matter and we again have a bareword; so that space matters here!

This trickiness is due to sort's flexible (ambiguous) interface, taking a LIST optionally preceded by a bareword, which need be a sub name. It ends up relying on the interpreter to sort out some of that and then we have to be precise.

Bixler answered 10/8, 2019 at 19:23 Comment(1)
@Fascicle Yes, thank you (just posted a comment with that but forgot it here), added to answerBixler

© 2022 - 2024 — McMap. All rights reserved.