Beefy Boxes and Bandwidth Generously Provided by pair Networks
good chemistry is complicated,
and a little bit messy -LW
 
PerlMonks  

sub scope question

by perl45036 (Novice)
on Feb 07, 2008 at 03:38 UTC ( #666724=perlquestion: print w/replies, xml ) Need Help??

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

#!/usr/bin/perl use strict; my @firstlist = qw(1 2 3 4 5); my @secondlist = qw(A B C D E); process_list(\@firstlist); process_list(\@secondlist); sub process_list { my($list_ref) = @_; sub get_list { return @$list_ref; } print_list(); } sub print_list { foreach my $item (get_list()) { print "LIST ITEM: $item\n"; } }
Output:
LIST ITEM: 1 LIST ITEM: 2 LIST ITEM: 3 LIST ITEM: 4 LIST ITEM: 5 LIST ITEM: 1 LIST ITEM: 2 LIST ITEM: 3 LIST ITEM: 4 LIST ITEM: 5
Why does the closure "get_list" not get updated to use the reference to @secondlist? I'm assuming there is a scope issue with the way "get_list" is declared. I understand there is a better way to write this with the use of a function that returns an anonymous sub but I'm not sure how to explain the issue with the code above. Better solution:
#!/usr/bin/perl use strict; my @firstlist = qw(1 2 3 4 5); my @secondlist = qw(A B C D E); process_list(\@firstlist); process_list(\@secondlist); my $get_list_sub; sub make_list_closure { my ($ref) = @_; return sub { return @$ref; }; } sub process_list { my($list_ref) = @_; $get_list_sub = make_list_closure($list_ref); print_list(); } sub print_list { foreach my $item ($get_list_sub->()) { print "LIST ITEM: $item\n"; } }

Replies are listed 'Best First'.
Re: sub scope question
by pc88mxer (Vicar) on Feb 07, 2008 at 03:51 UTC
    That's because defining named subroutines is done at compile time. Here's an illustrative example:

    sub a { sub x { print "I came from a\n"; } } sub b { sub x { print "I came from b\n"; } } x(); a(); x(); b(); x();

    This generates:

    I came from b I came from b I came from b

    To do things your way you can localize the subroutine name, i.e.:

    sub a { local(*x) = sub { "from a" }; doit(); } sub b { local(*x) = sub { "from b" }; doit(); } sub doit { print "I came ", x(), "\n"; }
Re: sub scope question
by shmem (Chancellor) on Feb 07, 2008 at 08:53 UTC
    In cases like that
    use warnings; use diagnostics;

    gives a clue:

    Variable "$list_ref" will not stay shared at - line 15 (#1) (W closure) An inner (nested) named subroutine is referencing a lexical variable defined in an outer subroutine. When the inner subroutine is called, it will probably see the valu +e of the outer subroutine's variable as it was before and during the *f +irst* call to the outer subroutine; in this case, after the first call t +o the outer subroutine is complete, the inner and outer subroutines will + no longer share a common value for the variable. In other words, the variable will no longer be shared. Furthermore, if the outer subroutine is anonymous and references a lexical variable outside itself, then the outer and inner subrouti +nes will never share the given variable. This problem can usually be solved by making the inner subroutine anonymous, using the sub {} syntax. When inner anonymous subs tha +t reference variables in outer subroutines are called or referenced, + they are automatically rebound to the current values of such variables.

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
      I have to admit, I was entirely perplexed as well by this until I realized that my in Perl acts like an actual executed statement, not merely a declaration (like it would be, for example, in C). As a result, you get a new instance of $list_ref every time the function is called -- which is why it doesn't stay shared.

      When I want to create a shared variable with long-term state like that (i.e., a C-style "static"), I put the applicable function definitions in a scope block with the my statement at the outer level -- e.g.:

      #!/usr/bin/perl -w use strict; my @firstlist = qw(1 2 3 4 5); my @secondlist = qw(A B C D E); process_list(\@firstlist); process_list(\@secondlist); { my $list_ref; sub process_list { ($list_ref) = @_; sub get_list { return @$list_ref; } print_list(); } } sub print_list { foreach my $item (get_list()) { print "LIST ITEM: $item\n"; } }

      which produces:

      LIST ITEM: 1 LIST ITEM: 2 LIST ITEM: 3 LIST ITEM: 4 LIST ITEM: 5 LIST ITEM: A LIST ITEM: B LIST ITEM: C LIST ITEM: D LIST ITEM: E

      as expected. Naturally, you could (per ikegami, should) also move the get_list() subroutine outside the scope of process_list() for clarity, since it doesn't really add value there -- but I left it as-is to minimize changes from your original program.

      Update: Strengthened some wording in response to comments from ikegami on Perl internals

        You say you get a new instance of $list_ref every time the function is called because my is not merely a declaration like in C. Yet you would get a new instance of local variables every time a function is called in C as well! Consider a re-entrant function, for example.

        In fact, as an optimization in Perl, you *don't* get a new instance of my variable every pass unless you do something that forces Perl to create a new variable. That's an implementation detail you shouldn't worry about, though.

        As for your suggestion to move my $list_ref, it seems to miss the point that nesting *named* functions has no benefit in Perl. Instead of trying to make the nested named function work, one should look at what one wants to achieve by nesting a named function and look for ways to meet that goal. (local *name = sub { ... }; is the likely answer.)

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others making s'mores by the fire in the courtyard of the Monastery: (6)
As of 2020-02-28 13:30 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    What numbers are you going to focus on primarily in 2020?










    Results (123 votes). Check out past polls.

    Notices?