Sort using reference to subroutine name?

Discussion in 'Perl Misc' started by Hein, Nashua NH, Aug 5, 2008.

  1. I have function which I'd like to call telling it how to sort some
    arrays.
    I'd like to do something like:

    my $sort_by_x_ref = sub { return ($x{$b} <=> $x{$a})};
    my $sort_by_y_ref = sub { return ($y{$b} <=> $y{$a})}

    sub print_sorted {
    my $sort_function = $_[0]->();
    print "Sorted $_[1]\n";
    foreach (sort {$sort_function} keys %some_array) {
    :
    print...
    :
    }

    &print_sorted ( $sort_by_x_ref, "by X");
    &print_sorted ( $sort_by_y_ref, "by Y" );

    That does not work. {&$sort_function}, and just $sort_function do not
    seem to work either.

    Can this be done? What is the syntax?

    My workaround is boring:

    sub print_sorted {
    print "Sorted $_[1]\n";
    foreach (sort {($_[0]) ? $x{$b} <=> $x{$a} : $y{$b} <=> $y{$a} } keys
    %some_array) {
    :
    print...
    :
    }
    &print_sorted ( 1, "by X");
    &print_sorted ( 0, "by Y" );

    Please advice,
    Hein.
    Hein, Nashua NH, Aug 5, 2008
    #1
    1. Advertising

  2. Hein, Nashua NH

    Ben Morrow Guest

    Quoth "Hein, Nashua NH" <>:
    >
    > I have function which I'd like to call telling it how to sort some
    > arrays.
    > I'd like to do something like:
    >
    > my $sort_by_x_ref = sub { return ($x{$b} <=> $x{$a})};
    > my $sort_by_y_ref = sub { return ($y{$b} <=> $y{$a})}
    >
    > sub print_sorted {
    > my $sort_function = $_[0]->();


    Why are you doing this? (What did you think it would do for you?) I'm
    fairly sure you just want

    my $sort_function = $_[0];

    here.

    > print "Sorted $_[1]\n";
    > foreach (sort {$sort_function} keys %some_array) {


    foreach (sort $sort_function keys %some_array) {

    except %some_array isn't an array, it's a hash. Using a block
    {$sort_function} will attempt to sort with a sub that returns
    $sort_function every time, which is unlikely to be successful.

    Note that the sort function will need to be compiled into the package
    that is current when 'sort' is called (or you could use the
    ($$)-prototyped variant, but that's both slow and obscure) as otherwise
    $a and $b won't work.

    I'm not quite sure why you're doing this: why not just pass a ref to the
    hash to sort by, and then use something like

    my $sort_by = $_[0];

    sort { $sort_by->{$a} <=> $sort_by{$b} } ...;

    ?

    > &print_sorted ( $sort_by_x_ref, "by X");
    > &print_sorted ( $sort_by_y_ref, "by Y" );


    Don't call subs with & unless you know what it does, and what it does is
    what you want (hint: it probably isn't).

    Ben

    --
    For the last month, a large number of PSNs in the Arpa[Inter-]net have been
    reporting symptoms of congestion ... These reports have been accompanied by an
    increasing number of user complaints ... As of June,... the Arpanet contained
    47 nodes and 63 links. [ftp://rtfm.mit.edu/pub/arpaprob.txt] *
    Ben Morrow, Aug 5, 2008
    #2
    1. Advertising

  3. On Aug 4, 11:49 pm, Ben Morrow <> wrote:
    > Quoth "Hein, Nashua NH" <>:
    >
    >
    >
    > > I have function which I'd like to call telling it how to sort some arrays.

    :
    > > my $sort_function = $_[0]->();


    > Why are you doing this? (What did you think it would do for you?) I'm
    > fairly sure you just want
    >
    >     my $sort_function = $_[0];


    I tried that first, but it didn't work for me.
    So I check some pod pages and googles around to find that suggestion
    in:
    http://www.perlmonks.org/index.pl?node_id=680130

    >     foreach (sort $sort_function keys %some_array) {
    >
    > except %some_array isn't an array, it's a hash.


    Right, sorry for the sloppy names. It was a hash.

    > {$sort_function} will attempt to sort with a sub that returns
    > $sort_function every time, which is unlikely to be successful.


    Yeah, that is probably critical, but I'm not sure I entirely undestand
    that.
    I had tried putting a debug "print qq(in sort $a $b\n)" withing the
    function indeed showed undefs for $a and $b :-(


    > Note that the sort function will need to be compiled into the package
    > that is current when 'sort' is called (or you could use the
    > ($$)-prototyped variant, but that's both slow and obscure) as otherwise
    > $a and $b won't work.


    That sounds like my problem.

    > I'm not quite sure why you're doing this: why not just pass a ref to the
    > hash to sort by, and then use something like


    Because I was thinking of various other sort funtions, not just
    varying by hash.

    >     my $sort_by = $_[0];
    >     sort { $sort_by->{$a} <=> $sort_by{$b} } ...;


    Right, I get that, and have used similar contructs before.

    > &print_sorted ( $sort_by_y_ref, "by Y" );
    >
    > Don't call subs with & unless you know what it does, and what it does is
    > what you want (hint: it probably isn't).


    I more or less know that, but got carried away in trying almost random
    solutions for my sort challenge, as reasoned (flawed reasoning :)
    solutions did not work.

    Thanks for the quick feedback!
    Hein.
    Hein, Nashua NH, Aug 5, 2008
    #3
  4. Hein, Nashua NH

    Ben Morrow Guest

    Quoth "Hein, Nashua NH" <>:
    > On Aug 4, 11:49 pm, Ben Morrow <> wrote:
    > > Quoth "Hein, Nashua NH" <>:
    > >
    > > > I have function which I'd like to call telling it how to sort some arrays.

    > :
    > > > my $sort_function = $_[0]->();

    >
    > > Why are you doing this? (What did you think it would do for you?) I'm
    > > fairly sure you just want
    > >
    > >     my $sort_function = $_[0];

    >
    > I tried that first, but it didn't work for me.
    > So I check some pod pages and googles around to find that suggestion
    > in:
    > http://www.perlmonks.org/index.pl?node_id=680130


    Hmm, there's some *seriously* bad advice on that page :). I think what
    you're not understanding is that $_[0]->() calls the function once,
    before you even start sorting: that's not what you want to do. You want
    to call the function *during* the sort, for each comparison, so you need
    to pass the actual function into sort somehow, not its return value.

    > >     foreach (sort $sort_function keys %some_array) {

    >
    > > {$sort_function} will attempt to sort with a sub that returns
    > > $sort_function every time, which is unlikely to be successful.

    >
    > Yeah, that is probably critical, but I'm not sure I entirely undestand
    > that.
    > I had tried putting a debug "print qq(in sort $a $b\n)" withing the
    > function indeed showed undefs for $a and $b :-(


    If you call sort with a block, like

    sort { STATEMENTS } LIST;

    then the STATEMENTS are compiled into a new anonymous sub. It's almost
    exactly the same as

    my $sub = sub { STATEMENTS };
    sort $sub LIST;

    or even

    sub sort_sub { STATEMENTS }
    sort sort_sub LIST;

    except that sort blocks are very slightly more efficient than ordinary
    anon subs. What you had,

    my $sub = sub { STATEMENTS };
    sort { $sub } LIST;

    is then trying to sort using a sub which returns the same subref for
    every comparison: $sub is never even called, so your carefully
    constructed comparison subs become useless :). You could write a sort
    block that explicitly calls the sub:

    my $sub = sub { STATEMENTS };
    sort { $sub->() } LIST;

    but that's really just a waste of time: if all your sort sub is going to
    do is call another function, you might as well pass that function to
    sort directly.

    > > Note that the sort function will need to be compiled into the package
    > > that is current when 'sort' is called (or you could use the
    > > ($$)-prototyped variant, but that's both slow and obscure) as otherwise
    > > $a and $b won't work.

    >
    > That sounds like my problem.


    No, it doesn't. I was talking about the case where you have

    package Foo;

    my $sub = sub { $a <=> $b };

    package Bar;

    my @list = sort $sub 2, 3, 1;

    in which case sort is setting $Bar::a and $Bar::b, while the sub is
    looking at $Foo::a and $Foo::b, so no useful comparisons get done (but
    for a different reason from before). This would only be a problem if you
    were passing in sort subs from a different package, which your example
    code wasn't doing.

    > > I'm not quite sure why you're doing this: why not just pass a ref to the
    > > hash to sort by, and then use something like

    >
    > Because I was thinking of various other sort funtions, not just
    > varying by hash.


    OK, that makes sense.

    > > Don't call subs with & unless you know what it does, and what it does is
    > > what you want (hint: it probably isn't).

    >
    > I more or less know that, but got carried away in trying almost random
    > solutions for my sort challenge, as reasoned (flawed reasoning :)
    > solutions did not work.


    When you're lost, randomly trying things is *very* rarely helpful.
    Re-reading the docs, adding 'warn' statements so you can see what is
    actually going on, and (eventually) posting to Usenet are much more
    likely to produce useful results :).

    Ben

    --
    Raise your hand if you're invulnerable.
    []
    Ben Morrow, Aug 5, 2008
    #4
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Casey
    Replies:
    3
    Views:
    843
    Casey
    Jan 30, 2004
  2. Navin
    Replies:
    1
    Views:
    666
    Ken Schaefer
    Sep 9, 2003
  3. Nath

    Sort array - subroutine help

    Nath, Aug 16, 2004, in forum: Perl Misc
    Replies:
    4
    Views:
    99
    Uri Guttman
    Aug 16, 2004
  4. Kirk Is
    Replies:
    5
    Views:
    93
    gnari
    Nov 26, 2004
  5. king
    Replies:
    5
    Views:
    173
Loading...

Share This Page