Open p5pRT opened 6 years ago
This applies from perl 5.8 - 5.26
The perldoc(sort) documentation has long had bugs in it\, relative to its suggestions for the syntax for sorting when using a list-generator function or a user-defined comparator function. It gives suggestions\, but the suggestions do not take into account the variety of syntaxes that can be used\, and their consequences.
The test program included below shows many of these effects. My suggestions: do not ever use parentheses around the sort arguments or around the list to be sorted\, since that can cause compiler errors or misinterpretation of the function names. Do not use the form &generatorfunc since that will cause a compiler error when the generatorfunc name has no underscore in it. To simplify usage\, use only these forms:
sort @list_to_be_sorted sort cmpfunc @list_to_be_sorted sort +genfunc @args_to_genfunc # list generator function sort cmpfunc +genfunc @args_to_genfunc # comparator function sort map map_func\, @args_to_map # built-in list generator (map) sort cmpfunc map map_func\, @args_to_map
Here are my comments on the specific text from perldoc(sort):
#------------------------------------------------------------------------ Warning: syntactical care is required when sorting the list returned from a function. If you want to sort the list returned by the function call find_records(@key)\, you can use:
@contact = sort { $a cmp $b } find_records @key; # ok @contact = sort +find_records(@key); # best\, with or without parens @contact = sort &find_records(@key); # requires parens @contact = sort(find_records(@key)); # worst\, requires inner parens\, # which must be adjacent to the # function name (otherwise the # function will be used as a # comparator function)!
If instead you want to sort the array @key with the comparison routine find_records() then you can use:
@contact = sort { find_records() } @key; # ok @contact = sort find_records(@key); # best\, with or without parens @contact = sort(find_records @key); # bad\, compiler warning if the # comparator function name has # no underscore @contact = sort(find_records (@key)); # worst\, if inner parens are # adjacent to function name\, # sort() will sort the output # of find_records(@key); # compiler warning if the # comparator function name has # no underscore #------------------------------------------------------------------------
Here is my test program to demonstrate some of the weird results when using parentheses\, with or without a space in front of them\, combined with different forms of arguments for sort().
#!/usr/bin/perl #****************************************************************************** # Test the use of sort with various user-defined and built-in functions. # Especially take into consideration placement of parentheses. # # The user-defined comparator functions (c and ncmp) do *not* have underscores # in their names\, which causes the parser to emit compile-time warnings # ("Unquoted string "COMPARATOR-NAME" may clash with future reserved word")\, # even though the function has been declared before being used. # # Also discovered: parentheses are required around the generator function's # arguments\, if the form &generator is used\, or a fatal compile-time error is # emitted ("Array found where operator expected"\, followed by "syntax error"). # # Finally\, the use or lack of use of parentheses and *the placement of the # parentheses* is critical to the correct interpretation of the arguments to # sort(). #******************************************************************************
use strict; use warnings;
use Test::More qw(no_plan);
my @list = qw( c b e d ); my $clist = [ sort @list ]; my $glist = [ sort "a"\, @list ];
sub g { return ( "a"\, @_ ) } # generate list sub c { return $a cmp $b } # sort comparator sub ncmp { return $a \<=> $b } # numeric comparator sub p { return @_ } # pass-through
#******************************************************************************
our @w; my $w = 0; BEGIN { $SIG{__WARN__} = sub { push @w\, "@_" } }
sub w { my ($lnum) = @_; return "WARN-$w" if $w[$w++] =~ /may clash.*$lnum/; die "unexpected: $w[$w - 1]\n"; }
sub e { return "COMPILE-ERROR" if $@ =~ /syntax error/; die "unexpected: \$@​\n"; }
sub compile { local $SIG{__WARN__} = sub {}; undef $@; eval $_[0]; }
like $^V\, qr/^( \x05\x08\x04 | \x05\x08\x08 | v5[.] ( 10[.]1 | 16[.]3 | 18[.]2 | 22[.]2 | 24[.]1 | 26[.]0 ) )\z/x\, 'perl version'; ok 1\, "";
is_deeply [ sort @list ]\, $clist\, " OK! sort L"; is_deeply [ sort( @list) ]\, $clist\, " OK! sort( L)"; is_deeply [ sort (@list) ]\, $clist\, " OK! sort (L)"; ok 1\, "";
is_deeply [ sort { $a cmp $b } @list ]\, $clist\, " OK! sort {\<=>} L"; is_deeply [ sort { c } @list ]\, $clist\, " OK! sort {c } L"; is_deeply [ sort { c() } @list ]\, $clist\, " OK! sort {c()} L"; ok 1\, "";
is_deeply [ sort( { $a cmp $b } @list) ]\, $clist\, "OK! sort( {\<=>} L)"; is_deeply [ sort( { c } @list) ]\, $clist\, "OK! sort( {c } L)"; is_deeply [ sort( { c() } @list) ]\, $clist\, "OK! sort( {c()} L)"; ok 1\, "";
is_deeply [ sort ({ $a cmp $b } @list) ]\, $clist\, "OK! sort ({\<=>} L)"; is_deeply [ sort ({ c } @list) ]\, $clist\, "OK! sort ({c } L)"; is_deeply [ sort ({ c() } @list) ]\, $clist\, "OK! sort ({c()} L)"; ok 1\, "";
is_deeply [ sort { $a cmp $b } g @list ]\, $glist\, "OK! sort {\<=>} g L"; is_deeply [ sort { c } g @list ]\, $glist\, "OK! sort {c } g L"; is_deeply [ sort { c() } g @list ]\, $glist\, "OK! sort {c()} g L"; ok 1\, "";
is_deeply [ sort( { $a cmp $b } g @list) ]\, $glist\, "OK! sort( {\<=>} g L)"; is_deeply [ sort( { c } g @list) ]\, $glist\, "OK! sort( {c } g L)"; is_deeply [ sort( { c() } g @list) ]\, $glist\, "OK! sort( {c()} g L)"; ok 1\, "";
is_deeply [ sort ({ $a cmp $b } g @list) ]\, $glist\, "OK! sort ({\<=>} g L)"; is_deeply [ sort ({ c } g @list) ]\, $glist\, "OK! sort ({c } g L)"; is_deeply [ sort ({ c() } g @list) ]\, $glist\, "OK! sort ({c()} g L)"; ok 1\, "";
compile ' sort &g @list '; ok $@\, " sort &g L @{[e]}"; is_deeply [ sort &g( @list) ]\, $glist\, "ok sort &g( L)"; is_deeply [ sort &g (@list) ]\, $glist\, "ok sort &g (L)"; ok 1\, "";
compile ' sort( &g @list ) '; ok $@\, " sort( &g L ) @{[e]}"; is_deeply [ sort( &g( @list)) ]\, $glist\, "ok sort( &g( L))"; is_deeply [ sort( &g (@list)) ]\, $glist\, "ok sort( &g (L))"; ok 1\, "";
compile ' sort (&g @list ) '; ok $@\, " sort (&g L ) @{[e]}"; is_deeply [ sort (&g( @list)) ]\, $glist\, "ok sort (&g( L))"; is_deeply [ sort (&g (@list)) ]\, $glist\, "ok sort (&g (L))"; ok 1\, "";
is_deeply [ sort +g @list ]\, $glist\, "OK! sort +g L"; is_deeply [ sort +g( @list) ]\, $glist\, "OK! sort +g( L)"; is_deeply [ sort +g (@list) ]\, $glist\, "OK! sort +g (L)"; ok 1\, "";
is_deeply [ sort( +g @list ) ]\, $glist\, "OK! sort( +g L )"; is_deeply [ sort( +g( @list)) ]\, $glist\, "OK! sort( +g( L))"; is_deeply [ sort( +g (@list)) ]\, $glist\, "OK! sort( +g (L))"; ok 1\, "";
is_deeply [ sort (+g @list ) ]\, $glist\, "OK! sort (+g L )"; is_deeply [ sort (+g( @list)) ]\, $glist\, "OK! sort (+g( L))"; is_deeply [ sort (+g (@list)) ]\, $glist\, "OK! sort (+g (L))"; ok 1\, "";
is_deeply [ sort c @list ]\, $clist\, "OK! sort c L"; is_deeply [ sort c( @list) ]\, $clist\, "OK! sort c( L)"; is_deeply [ sort c (@list) ]\, $clist\, "OK! sort c (L)"; ok 1\, "";
is_deeply [ sort( c @list ) ]\, $clist\, " sort( c L ) @{[w __LINE__]}"; is_deeply [ sort( g( @list)) ]\, $glist\, " sort( g( L)) NOT-COMPARATOR"; is_deeply [ sort( c (@list)) ]\, $clist\, " sort( c (L)) @{[w __LINE__]}"; ok 1\, "";
is_deeply [ sort (c @list ) ]\, $clist\, " sort (c L ) @{[w __LINE__]}"; is_deeply [ sort (g( @list)) ]\, $glist\, " sort (g( L)) NOT-COMPARATOR"; is_deeply [ sort (c (@list)) ]\, $clist\, " sort (c (L)) @{[w __LINE__]}"; ok 1\, "";
is_deeply [ sort c g @list ]\, $glist\, "OK! sort c g L"; is_deeply [ sort c( g @list) ]\, $glist\, "OK! sort c( g L)"; is_deeply [ sort c ( g @list) ]\, $glist\, "OK! sort c (g L)"; ok 1\, "";
is_deeply [ sort c +g @list ]\, $glist\, "OK! sort c +g L"; is_deeply [ sort c( +g @list) ]\, $glist\, "OK! sort c( +g L)"; is_deeply [ sort c (+g @list) ]\, $glist\, "OK! sort c (+g L)"; ok 1\, "";
is_deeply [ sort( c g @list )]\, $glist\, " sort( c g L ) @{[w __LINE__]}"; is_deeply [ sort( p( g @list))]\, $glist\, " sort( p( g L)) NOT-COMPARATOR"; is_deeply [ sort( c ( g @list))]\, $glist\, " sort( c (g L)) @{[w __LINE__]}"; ok 1\, "";
is_deeply [ sort (c g @list ) ]\, $glist\, " sort (c g L ) @{[w __LINE__]}"; is_deeply [ sort (p( g @list)) ]\, $glist\, " sort (p( g L)) NOT-COMPARATOR"; is_deeply [ sort (c (g @list)) ]\, $glist\, " sort (c (g L)) @{[w __LINE__]}"; ok 1\, "";
my @nlist = ( 50\, 51\, 49 ); my @slist = (qw( 2 3 1 )); my $snlist = [ sort @nlist ]; my $sslist = [ sort @slist ];
is_deeply [ sort map chr\, @nlist ]\, $sslist\, "OK! sort map mf\, L"; is_deeply [ sort +map chr\, @nlist ]\, $sslist\, "OK! sort +map mf\, L"; is_deeply [ sort ncmp map $_\, @nlist ]\, $snlist\, "OK! sort c map mf\, L"; is_deeply [ sort ncmp +map $_\, @nlist ]\, $snlist\, "OK! sort c +map mf\, L"; ok 1\, "";
is_deeply [ sort( map chr\, @nlist) ]\, $sslist\, "ok sort( map mf\, L)"; is_deeply [ sort( +map chr\, @nlist) ]\, $sslist\, "ok sort( +map mf\, L)"; is_deeply [ sort( ncmp map $_\, @nlist) ]\, $snlist\, " sort( c map mf\, L) @{[w __LINE__]}"; is_deeply [ sort( ncmp +map $_\, @nlist) ]\, $snlist\, " sort( c +map mf\, L) @{[w __LINE__]}"; ok 1\, "";
is_deeply [ sort ( map chr\, @nlist) ]\, $sslist\, "ok sort ( map mf\, L)"; is_deeply [ sort ( +map chr\, @nlist) ]\, $sslist\, "ok sort ( +map mf\, L)"; is_deeply [ sort (ncmp map $_\, @nlist) ]\, $snlist\, " sort (c map mf\, L) @{[w __LINE__]}"; is_deeply [ sort (ncmp +map $_\, @nlist) ]\, $snlist\, " sort (c +map mf\, L) @{[w __LINE__]}"; ok 1\, "";
is $#w\, $w-1\, "warning_count";
# done_testing(); # not available in perl 5.8 is "DONE"\, "DONE"\, "done";
#******************************************************************************
Migrated from rt.perl.org#132716 (status was 'new')
Searchable as RT132716$