vitaly has asked for the wisdom of the Perl Monks concerning the following question:

In the sub below I want to pass an aditional parameter, which will tell based on which field in the string with | delimeter sorting will be done. I tried to use @_ but experienced problems. I want something like my @sorted_xx = sort sorter (@xx, $sorting_field); Probably I want what is impossible. Thanks, Vitaly
----------------------------------------------------
my @xx = ( 'dfb|cy|nmju', 'dfb|my|jhiho', 'aaa|zz|gggg' ); my @sorted_xx = sort sorter @xx; print Dumper \@xx; print Dumper \@sorted_xx; sub sorter { my @arr_a = split /\|/, $a; my @arr_b = split /\|/, $b; while ( @arr_a ) { my $sa = shift @arr_a; my $sb = shift @arr_b; my $comparison = ( $sa cmp $sb ); return $comparison if $comparison; } return 0; }

Replies are listed 'Best First'.
Re: Pass additional params into sub which create sorting rules
by moritz (Cardinal) on Dec 02, 2007 at 17:26 UTC
    I think you want something like
    my @sorted = sort { sorter($a, $b, $offset) } @list; # ... sub sorter { my ($left, $right, $offset) = @_; ... }

    Update: working, if somewhat simplified example: sort by the second column.

    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; my @xx = ( 'dfb|cy|nmju', 'dfb|my|jhiho', 'aaa|zz|gggg' ); my @sorted_xx = sort { sorter($a, $b, 1) } @xx; print Dumper \@xx; print Dumper \@sorted_xx; sub sorter { my ($left, $right, $offset) = @_; my @arr_a = split /\|/, $left; my @arr_b = split /\|/, $right; return $arr_a[$offset] cmp $arr_b[$offset]; }
Re: Pass additional params into sub which create sorting rules
by shmem (Chancellor) on Dec 02, 2007 at 17:29 UTC
    I'd probably do that with a Schwartzian Transform, using a lexical to indicate which field I want the array to be sorted upon:
    my @xx = ( 'dfb|cy|nmju', 'dfb|my|jhiho', 'aaa|zz|gggg' ); my $sort_field = 1; my @sorted_xx = map { $_->[1] } sort {$a->[0] cmp $b->[0]} map { [ (split /\|/, $_)[$sort_field], $_ ] } @xx;

    which can be wrapped up in a sub into which to pass the delimiter and the $sort_field variable, as well as the array to sort (probably as a reference).

    Of course, I could also set up a sub to pass to sort using a global flag

    sub sorter { my @arr_a = split /\|/, $a; my @arr_b = split /\|/, $b; return $arr_a[$sort_field] cmp $arr_b[$sort_field]; }

    but that would mean splitting each array element over and over for each comparison.

    See also the recent thread Sorting a (very) large file.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: Pass additional params into sub which create sorting rules
by kyle (Abbot) on Dec 02, 2007 at 21:32 UTC

    Say, that code looks familiar! (I find that pretty amusing.)

Re: Pass additional params into sub which create sorting rules
by sgifford (Prior) on Dec 04, 2007 at 06:35 UTC
    A useful technique is to create a "sorter factory", which will create a new sub which remembers the sort parameters, then return that sub to be used by sort. In Perl parlance, this is called storing the parameters in a lexical variable and creating a sub which closes around that lexical. For example:
    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; my @xx = ( 'dfb|cy|nmju', 'dfb|my|jhiho', 'aaa|zz|gggg' ); my $sorter = make_sorter(2); my @sorted_xx = sort $sorter @xx; print Dumper \@xx; print Dumper \@sorted_xx; sub make_sorter { my($col)=@_; return sub { my @arr_a = split /\|/, $a; my @arr_b = split /\|/, $b; return $arr_a[$col] cmp $arr_b[$col]; }; }