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

I'm writing a simple database-type application in which the data needs to be sorted by whichever column the user selects. I'm having trouble figuring out a way to dynamically determine which sort algorithm to use in the main "sort" statement. I'm maintaining the session state, including the variable $sortname, which contains the name of the function containing the appropriate sort function.

If the user wants to sort the data in descending order by the "FirstName" column, $sortname contains "firstname_descending". This would seem useful, since I can say the following:

my @order = sort firstname_descending keys %data;

However, what I WANT to use is:

my @order = sort $sortname keys %data;

That way, if $sortname contains "firstname_descending", this function would still be called:

sub firstname_descending { $data{$b}{'FirstName'} cmp $data{$a}{'FirstName'}; }
Why doesn't that work? How can I use this type of sort architecture?

Replies are listed 'Best First'.
RE: Dynamic sort algorithm selection
by btrott (Parson) on Nov 15, 2000 at 05:46 UTC
    What version of Perl are you using? This works for me:
    #!/usr/bin/perl -w use strict; my $sortname = shift; print join ' ', sort $sortname 1..20; sub num { $a <=> $b } sub alpha { $a cmp $b }
    Save this as, and...
    % num 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 % alpha 1 10 11 12 13 14 15 16 17 18 19 2 20 3 4 5 6 7 8 9
    This is consistent w/ the sort docs:
    SUBNAME may be a scalar variable name (unsubscripted), in which case the value provides the name of (or a reference to) the actual subroutine to use. In place of a SUBNAME, you can provide a BLOCK as an anonymous, in-line sort subroutine.
    However, I do seem to remember running into your situation in the past. I don't know what version of Perl I was using then; probably 5.004 or some such. I'm now using 5.005_03, which could explain it.

    If you are using a real database, though, I would suggest that you try using "order by", as it's likely to be faster than anything you can do yourself, particularly if you have your columns indexed.

RE (tilly) 1: Dynamic sort algorithm selection
by tilly (Archbishop) on Nov 15, 2000 at 05:58 UTC
    What you want to do is accept a parameter input as the name of an arbitrary function to call? That is a symbolic reference, and is generally a bad idea (particularly in a CGI script - see some of Ovid's posts on that).

    Instead I would suggest that you do something like this (untested):

    use Carp; # So we can get verbose error messages # Time passes sub ret_str_sorter { my $field = shift; my $direction = shift; if ('ascending' eq $direction) { return sub { $data{$a}{$field} cmp $data{$b}{$field}; }; } elsif ('descending' eq $direction) { return sub { $data{$b}{$field} cmp $data{$a}{$field}; }; } else { confess("Direction '$direction' not understood"); } } # Time passes my $sorter = ret_str_sorter($field, $direction); my @ordered = sort $sorter keys %data;
    ObRandomTip: Rather than working with an array of keys into a hash, instead restructure to have an array of anonymous hashes. This will generally lead to a cleaner design and no need to do things like pass the hash definition in through a global.
RE: Dynamic sort algorithm selection
by quidity (Pilgrim) on Nov 15, 2000 at 05:42 UTC

    This does actually work the way you expected, set up some subs with known names, then give the sort routine a variable containing that name:

    sub ascii {$a cmp $b} sub alpha {lc($a) cmp lc($b)} @a = qw(a A b B D d c C); $subname = 'ascii'; @sorted = sort $subname @a; print @sorted; $subname = 'alpha'; @sorted = sort $subname @a; print @sorted;

    If you are having trouble with an earlier version of perl, then first try upgrading, failing that, you can always use eval:

     eval '@sorted = sort ' . $subname . ' @unsorted';

    But that's icky, so try not to do that.

RE: Dynamic sort algorithm selection
by dchetlin (Friar) on Nov 15, 2000 at 05:53 UTC
    That should work just fine as you have it (and has worked that way as far back as 5.004, at least). You can have the name of the subroutine in the scalar passed to sort.

    Not only that, but you can go one better. You can pass sort a reference to a subroutine. This is safer and cleaner and faster than using the name of the sub.

    my $sortref = \&firstname_descending; my @order = sort $sortref keys %data;

    For best results, combine this approach with a dispatch table that does your subroutine lookup.


RE: Dynamic sort algorithm selection
by Fastolfe (Vicar) on Nov 15, 2000 at 19:10 UTC
    I might implement that something like this:
    %SORT_INFO = ( 'firstname' => sub { $a->{FirstName} cmp $b->{FirstName} }, 'firstname_desc => sub { $b->{FirstName} cmp $a->{FirstName} }, ... ); ... if (exists $SORT_INFO{param('sort_by')}) { @sorted = sort $SORT_INFO{param('sort_by')} @unsorted; } elsif (param('sort_by')) { die "Unknown sort_by item: " . param('sort_by'); } else { @sorted = @unsorted; }
Re: Dynamic sort algorithm selection
by Sentinel (Initiate) on Nov 16, 2000 at 14:09 UTC
    The problem with my situation was simple, and no one thought of it or caught it, including me. I had

    use strict;

    in effect, which includes

    use strict 'refs';

    No dice on the sort subroutine's reference under those conditions. Sorry for the turmoil. I'll go back into my hole in the wall now.
RE: Dynamic sort algorithm selection
by jptxs (Curate) on Nov 15, 2000 at 07:15 UTC

    Being a Database bigot, I point out to you that almost every RDBMS has a sort mechanism, and you night want to let the Database do the sorting. Just a thought.

    <myExperience> $mostLanguages = 'Designed for engineers by engineers.'; $perl = 'Designed for people who speak by a linguist.'; </myExperience>
RE: Dynamic sort algorithm selection
by brick (Sexton) on Nov 15, 2000 at 07:35 UTC
    So basically you want a pointer to a function.

    I'm not too sure how to do that..I know that you
    can create/use pointers within perl, but I haven't
    dug through my tchrist notes in too long to even
    have a clue of remembering how.

    As far as making pointers to functions...couldn't
    you just make a simple if/else or case based on the
    designation variable to avoid the whole headache?
    TIMTOWTDI, and all that, I guess.

    What algorithm are you looking to use for your
    array sort? The first glance made it look like a
    bubble sort, which is fine if you aren't trying for
    high performance. Otherwise, _Algorithms and Data
    Structures_ by Corman-- aka the white cookbook, is
    a good place to get a lot of non-language specific
    psuedocode for just about every basic sorting option.

    good luck, -B.
      I am the -- vote, and I voted -- because there are several accurate answers and yours did not even meet the technical bar of knowing what the construct you want is called (reference), realizing that constructing code had already been posted (eg by me), or realizing that the person was asking about Perl's native sort function. (Indeed if you need to roll your own then you probably don't want to use a comparison function written in Perl!)

      Had this been something like CMonks or PascalMonks I would have been voting ++, but it is not and so I didn't.

        That's cool...I'm not looking to start a pissing
        contest, but the question didn't have any replies
        when I started my post, so I didn't have the
        benefit of your code.

        As far as the difference between a pointer and a
        reference...dude, when it comes down to it, aren't
        they really just the same thing? Semantically, yes,
        they're different. Under the hood? I pass things by
        reference, I give you the address. I use a pointer,
        it holds the address of something. I make a reference
        and I point you to a seperate chunk of code. I'm not
        a complete chimp on that respect. I haven't had the
        time or inclination to go look at whether or not the
        interpreter makes full substitutions at references or
        jumps to the sub when everything's said and done.
        That's deeper than I've needed to be for a long time.

        I _am_ an Acolyte, though, and one who does not get to
        code nearly as much as he wants to, so it is my bad
        for missing the pretty obvious reference to the
        native sort. I shouldn't have done that. I've always
        had that inherent distrust of someone else's code that
        I haven't thoroughly looked examined, just a leftover
        from school, and that's why I made the comment about
        picking a sorting algorithm. I know those ingrown ones
        are usually good at large data sets, but I like to pull
        off the lid and see _exactly_ how it's working inside,
        make sure it fits. You can't blame a guy for that.

        ever learning,