Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

Scoping issue when sorting with subroutines

by Dave05 (Beadle)
on Apr 12, 2002 at 14:47 UTC ( #158590=perlquestion: print w/ replies, xml ) Need Help??
Dave05 has asked for the wisdom of the Perl Monks concerning the following question:

Monks,

I want to do complex sorts on hash values, so I am using sort subroutines to do the comparisons. Although this works fine, I would like to move the 'up' and 'down' subs outside the scope of the 'sort_em' sub and pass in the %nos hash explicitly. This would make the sorting subs much more re-usable because I could then access them from different places, I could even hide them away in a module. Is this possible?

#!/usr/bin/perl # sort_em.plx use warnings; use strict; my %numbers = qw(one 1 two 2 three 3 four 4 five 5 six 6 seven 7 eight + 8); sort_em(\%numbers); sub sort_em { our %nos = %{$_[0]}; print "UP:\n"; print join "\n", sort up keys %nos; print "\n\nDOWN:\n"; print join "\n", sort down keys %nos; sub up { $nos{$a} <=> $nos{$b} } sub down { $nos{$b} <=> $nos{$a} } }

Comment on Scoping issue when sorting with subroutines
Download Code
Re: Scoping issue when sorting with subroutines
by strat (Canon) on Apr 12, 2002 at 14:58 UTC
    If you are using a "global" variable (with use vars or our in perl5.6), you can accept it via $packagename::nos{$a}.

    If %nos is a local-Variable (my), you have to give it (or better a reference to it) to the subs &up and &down.

    Best regards,
    perl -le "s==*F=e=>y~\*martinF~stronat~=>s~[^\w]~~g=>chop,print"

Re: Scoping issue when sorting with subroutines
by perlplexer (Hermit) on Apr 12, 2002 at 14:59 UTC
    Yes, you can do that; however, you should have a darn good reason for doing so. Why? Because your code will be calling a sub for each and every comparison, which is rather expensive and will slow down your program.
    use warnings; use strict; my %numbers = qw(one 1 two 2 three 3 four 4 five 5 six 6 seven 7 eight + 8); sort_em(\%numbers); sub sort_em { our %nos = %{$_[0]}; print "UP:\n"; print join "\n", sort { up(\%nos) } keys %nos; print "\n\nDOWN:\n"; print join "\n", sort { down(\%nos) } keys %nos; } sub up { $_[0]->{$a} <=> $_[0]->{$b} } sub down { $_[0]->{$b} <=> $_[0]->{$a} }

    --perlpelxer
Re: Scoping issue when sorting with subroutines
by erikharrison (Deacon) on Apr 12, 2002 at 15:07 UTC

    Sure it is! Just put the sub declarations in another package, use package at the top of your main code, and then use a fully qualified package name in place of up and down. However, since the code for these two subs is small, and refers to a specific variable in a wider scope, this may not be a good idea. Here is a rewrite of the sort_em sub that may point out why.

    sub sort_em { my %nos = @_; #Don't make a ref just to deref it, just #pass the hash. Use 'my' to indicate #lexical scope print "UP:\n"; print join "\n", sort { $nos{$a} <=> $nos{$b} } keys %nos; #just p +ass in short sorting routines on the line #of the sort print "\n\nDOWN:\n"; print join "\n", sort { $nos{$b} <=> $nos{$a} } keys %nos; #same a +s above }

    Note that the declaration of the subs up and down inside the braces of sort_em does not make them lexically scoped.

    Cheers,
    Erik
Re: Scoping issue when sorting with subroutines
by Fletch (Chancellor) on Apr 12, 2002 at 15:16 UTC

    Keep in mind that perl currently doesn't do nested subs (this is the souce of many a warning for those using Apache::Registry with mod_perl; see the mod_perl guide for a detailed explanation).

    You could possibly return a list of coderefs that you create and use sort_em more as a sort routine factory.

    sub sort_em { my $src = shift; return { up => sub { $src->{$a} <=> $src->{$b} }, down => sub { $src->{$b} <=> $src->{$a} }, } } my %numbers = ( qw( a 5 c 4 d 1 ) ); my $num_sorts = sort_em( \%numbers ); print "UP:\n", join( "\n", map { "$_ $numbers{$_}" } sort { $num_sorts->{up}->() } keys %numbers ), "\nDOWN\n", join( "\n", map { "$_ $numbers{$_}" } sort { $num_sorts->{down}->() } keys %numbers ), "\n";
      > Keep in mind that perl currently doesn't do nested subs
      Ack, how un-DWIM!
      sub foo { my $x = "a var in foo()"; sub bar { print "\$x is - [$x]\n"; } } bar(); __output__ $x is - []
      But if we use a closure, all is well with the scope of $x
      sub foo { my $x = "a var in foo()"; return sub { print "\$x is - [$x]\n"; } } foo()->(); __output__ $x is - [a var in foo()]
      I was vaguely aware of the fact that nested subs in perl were broken (i.e scoped to current package, not current sub) and now I know the full extent (but I guess the logic should've followed really). Oh well, not much longer til Perl6 anyway ...
      HTH

      broquaint

Re: Scoping issue when sorting with subroutines
by Dave05 (Beadle) on Apr 12, 2002 at 17:10 UTC
    OK,

    So I can pass the hash or whatever to my routine:

    print join "\n", sort up(\%nos) } keys %nos;
    which is cool. Unfortunately, in stripping down my code to present it to the Monks, I actually stripped out the main source of my problem (doh).

    The reason I want to use a sort subroutine is that I don't know how I want to sort the thing until runtime. So I want to call my routine like this:

    foreach my $id (sort $by_sort_method keys %contents) {
    And I'd like to pass the %contents hash to the subroutine.

    How do I do that?

      Use something like this to create $by_sort_method:
      sub create_sorter { my $contents = shift; return sub { # sort however you want here $contents{$a}{FOO} cmp $contents{$b}{FOO} }; } my $by_sort_method = create_sorter \%contents; foreach my $id (sort $by_sort_method keys %contents) { ... }
Re: Scoping issue when sorting with subroutines
by extremely (Priest) on Apr 12, 2002 at 19:17 UTC
    Maybe something like this is what you wanted? It is rather on the dark side of the force but it may be what you want.
    #!/usr/bin/perl -w use strict; my @l = (2, 4, 19, 3, 15, 30, 1, 31, 14); print sort {&{bysort("up")}} @l; print $/; print sort {&{bysort("dn")}} @l; print $/; sub bysort { my $by = shift; if ($by eq "up") { return sub { $a <=> $b }; } elsif ($by eq "dn") { return sub { $b <=> $a }; } else { return sub { $a cmp $b }; } }

    --
    $you = new YOU;
    honk() if $you->love(perl)

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://158590]
Approved by Kanji
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others imbibing at the Monastery: (6)
As of 2014-10-22 01:06 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (112 votes), past polls