You don't need a eval to do this, and it's easier to do without it. You basically want a higher order function that composes other functions, and all of the functions are going to get the same arguments.
Starting from the finished product, you might have a sort subroutine like this, where the return value is the first thing that returns true (which should be -1 or 1).
sub sortsub {
$a cmp $b ||
other_function( $a, $b ) ||
...
}
That's the same as writing it as a collection of subroutines that get arguments instead of using globals though:
sub sortsub {
string_compare( $a, $b ) ||
other_function( $a, $b ) ||
...
}
But I can rewrite that sortsub to take a list of subroutine references as its arguments. Now my sortsub goes through each subroutine and returns when it finds one that returns a true value. If it finds 0 (meaning that sub thought the elements were equal), it goes onto the next subroutine.
sub sortsub {
my @subs = @_;
foreach my $sub ( @subs )
{
my $result = $sub->($a, $b);
return $result if $result;
}
}
I can't use this in sort yet because I can't pass it arguments. I can, however, modify it to return a subroutine reference I can use:
sub make_sort_sub {
my @subs = @_;
return sub {
foreach my $sub ( @subs )
{
my $result = $sub->($a, $b);
return $result if $result;
}
};
}
Now, I basically make the reference then use it in my sort block.
my $sort_sub = make_sort_sub( @sub_references );
my @sorted = sort { $sort_sub->() } @stuff;
And here's a full demo, using it with numbers, then strings, then a user-defined sort subroutine. I don't need an eval for any of it.
#!/usr/bin/perl
# pre-defined common sort subroutines
my %Subs = (
numeric_descending => sub { $b <=> $a },
numeric_ascending => sub { $a <=> $b },
string_descending => sub { $b cmp $a },
string_ascending => sub { $b cmp $a },
case_insensitive => sub { "\L$a" cmp "\L$b" },
);
sub make_sort_sub {
my @subs = @_;
return sub {
foreach my $sub ( @subs )
{
my $result = $sub->($a, $b);
return $result if $result;
}
};
}
# numbers
{
my @use_these_subs = map { $Subs{$_} } qw(numeric_descending);
my $sort_sub = make_sort_sub( @use_these_subs );
my @sorted =
sort { $sort_sub->() } qw( 1 10 11 100 2 12 21 3 31 300 4 5 6 66 7
+ 71 );
print "@sorted\n";
}
# strings
{
my @use_these_subs = map { $Subs{$_} } qw(case_insensitive string_asce
+nding);
my $sort_sub = make_sort_sub( @use_these_subs );
my @sorted =
sort { $sort_sub->() } qw( Fred fred FReD Barney barney Betty BETT
+Y );
print "@sorted\n";
}
# strings by length with user defined subroutine
{
my @use_these_subs = ( sub { length $a <=> length $b } );
push @use_these_subs, map { $Subs{$_} } qw(string_ascending);
my $sort_sub = make_sort_sub( @use_these_subs );
my @sorted =
sort { $sort_sub->() } qw( Fred fred FReD Barney barney Betty BETT
+Y );
print "@sorted\n";
}
|