Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
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 browsing the Monastery: (11)
As of 2015-02-27 13:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    On my keyboard, Caps lock is:








    Results (445 votes), past polls