m.y has asked for the wisdom of the Perl Monks concerning the following question:

I have 5 functions which all perform similar tasks and I would like to have 1 function to replace the 5 individual functions.
Three function have a sort like this:
for my $key (sort { $ref->{$b}->[CONSTANT] <=> $ref->{$a}->[CONSTANT] +} keys %$ref)
where CONSTANT is a different constant in each of the 3 functions. And the other two have a sort like this:
for my $key (sort { $ref->{$b} <=> $ref->{$a} } keys %$ref)
I could make a generic function if someone could help me figure out how to do the sort in it..

thank you

Replies are listed 'Best First'.
Re: Sorting question
by apl (Monsignor) on Feb 12, 2008 at 18:43 UTC
    Let's assume sub GeneralizedFunction is passed $flag (with possible 'A', 'B', 'C', 'D', 'E') which specifies which sort routine to use. Let's also assume you have sort functions Sort1, Sort2, Sort3, Sort4 and Sort5.

    You could then write:

    use strict; use warnings; my %Functions = ( 'A' => { func => \&Sort1 }, 'B' => { func => \&Sort2 }, 'C' => { func => \&Sort3 }, 'D' => { func => \&Sort4 }, 'E' => { func => \&Sort5 }, ); sub GeneralizedFunction { my ( $flag ) = @_; # some processing if ( defined $Functions{ $flag } ) { $Functions{ $flag }{func}->(); # generalized sort } # other processing }
    I warn you this is untested, but it's very similar to code I use with great frequency.
Re: Sorting question
by davido (Cardinal) on Feb 12, 2008 at 18:39 UTC

    You could pass your sort routine as a coderef.


    Dave

Re: Sorting question
by kyle (Abbot) on Feb 12, 2008 at 18:48 UTC

    Maybe a dispatch table would help.

    sub thing { my ( $ref, $sort_by ) = @_; my %sort_sub = ( thing1 => sub { $ref->{$a} <=> $ref->{$b} }, thing2 => sub { $ref->{$a}->[CON] <=> $ref->{$b}->[CON] }, etc => sub { 'and so forth' }, ); my $sorter = $sort_sub{$sort_by}; for my $key ( sort $sorter keys %{$ref} ) { # ... } }

    Then you call it as thing( $ref, 'thing1' ), for example.

      In this case it's unlikely to matter, but the sort subroutines are closures which means that they'll remember $ref and $a/$b will be package variables of the package in which the subroutines are defined. That means that if the subroutines are used in a sort in another package, the wrong $a/$b variables will be used. In order to solves that the ($$) prototype has special meaning. With that prototype $a and $b will be passed to the subroutine, and you use $_[0] and $_[1] instead:

      sub ($$) { $ref->{$_[0]} <=> $ref->{$_[1]} }
      An example:
      use strict; no warnings; # So many that they hide the output. { package Foo; sub doit { my ($sorter, @keys) = @_; return sort $sorter @keys; } } my @foo = (3, 1, 2); print '$a: ', join(' ', Foo::doit(sub { $a <=> $b }, @foo)), "\n"; print '@_: ', join(' ', Foo::doit(sub ($$) { $_[0] <=> $_[1] }, @foo)) +, "\n"; __END__ $a: 3 1 2 @_: 1 2 3
      This becomes useful (necessary) if the sort routines are abstracted further.

      Update: Fixed several typos.

      lodin

Re: Sorting question
by thundergnat (Deacon) on Feb 12, 2008 at 19:53 UTC

    Maybe something like this? (Contrived example)

    use warnings; use strict; my %hash = ( 'Bob' => { 'best' => 9, 'worst' => 5 }, 'Steve' => { 'best' => 5, 'worst' => 1 }, 'Mark' => { 'best' => 10, 'worst' => 4 }, 'Dave' => { 'best' => 7, 'worst' => 3 } ); print "\npeople by best\n"; for my $key ( sort_by( \%hash, 'best' ) ) { print "$key\n" } print "\npeople by worst\n"; for my $key ( sort_by( \%hash, 'worst' ) ) { print "$key\n" } print "\nproperties\n"; for my $key ( sort_by( $hash{'Bob'}, undef ) ) { print "$key\n" } sub sort_by { my ( $ref, $constant ) = @_; if ( defined $constant ) { return sort { $ref->{$b}->{$constant} <=> $ref->{$a}->{$consta +nt} } keys %$ref; } else { return sort { $ref->{$b} <=> $ref->{$a} } keys %$ref; } }