Sort using reference to subroutine name?

  • Thread starter Hein, Nashua NH
  • Start date
H

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]->();
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.
 
B

Ben Morrow

Quoth "Hein said:
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
 
H

Hein, Nashua NH

Quoth "Hein, Nashua NH" <[email protected]>:


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.
 
B

Ben Morrow

Quoth "Hein said:
Quoth "Hein said:
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.
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.
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.
Because I was thinking of various other sort funtions, not just
varying by hash.

OK, that makes sense.
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top