Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

How to access a variable inside subroutine?

by pritesh_ugrankar (Monk)
on Sep 05, 2020 at 18:41 UTC ( #11121379=perlquestion: print w/replies, xml ) Need Help??

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

Respected Monks,

I am trying to print a variable in the subroutine outside the subroutine block.

use 5.030; sub counter { my @nums = (1..500); state $add= 0; for my $num(@nums) { $add += $num} } say "$add"; https://www.perlmonks.org/?node_id=11121379;displaytype=edit

I get an error stating "Global symbol "$add" requires explicit package name". How do I fix this? Kindly help.

Replies are listed 'Best First'.
Re: How to access a variable inside subroutine?
by jeffenstein (Friar) on Sep 05, 2020 at 19:03 UTC

    Hello Pritesh,

    Even though the variable is static, it is still only visible within the block where it was declared. In your example, the variable still exists, but inaccessible from outside the counter subroutine

    Hope this helps.

Re: How to access a variable inside subroutine?
by LanX (Cardinal) on Sep 05, 2020 at 19:48 UTC
Re: How to access a variable inside subroutine?
by haukex (Bishop) on Sep 05, 2020 at 19:20 UTC

    jeffenstein has said why it doesn't work, I'm wondering what you're trying to achieve? Depending on that, there are several possible solutions, one would be to have the subroutine return the value of $add so you can access it outside the subroutine. Another, though less common in Perl, would be to have the subroutine modify its arguments. And although I hesitate to mention it, you could also use a global variable.

Re: How to access a variable inside subroutine?
by kcott (Bishop) on Sep 06, 2020 at 01:35 UTC

    G'day pritesh_ugrankar,

    Some preamble:

    Your 'use 5.030;' will give you an automatic 'use strict;' but not a 'use warnings;'. It would've been better to start with:

    use 5.030; use warnings;

    For the code you've posted, you only need 'use 5.010;' for state and say, see perl5100delta; and 'use 5.012;' to get the 'use strict;', see use.

    In the following, I've used an alias of mine I commonly use for testing:

    $ alias perle alias perle='perl -Mstrict -Mwarnings -Mautodie=:all -MCarp::Always -E +'

    and, my Perl version is 5.32.0:

    $ perl -v | head -2 | tail -1 This is perl 5, version 32, subversion 0 (v5.32.0) built for cygwin-th +read-multi

    Here's an example of what you could have done that makes sense of the use of state (using slightly easier to follow numbers):

    $ perle ' say counter() for 1..3; sub counter { state $add; $add += $_ for (1..5); return $add; } ' 15 30 45

    You could've also assigned the return value and used it later. This example also shows two $add variables, used in different lexical scopes, with no conflict.

    $ perle ' my $add; for (1..3) { $add = counter(); say "... other processing here ..."; say $add; } sub counter { state $add; $add += $_ for (1..5); return $add; } ' ... other processing here ... 15 ... other processing here ... 30 ... other processing here ... 45

    — Ken

Re: How to access a variable inside subroutine?
by jcb (Vicar) on Sep 05, 2020 at 19:44 UTC

    This should do what you seem to be trying to do: (untested)

    use strict; use warnings; sub counter { my @nums = (1..500); our $add = 0; for my $num (@nums) { $add += $num } } { our $add; say $add; }

    This uses our to establish lexical aliases to a global variable "add" briefly as needed. For a small program, or within a module, this can be a useful technique, but please be careful not make gratuitous uses of global variables across larger programs.

    If you need more than one counter, objects are a better approach: (also untested)

    package Acme::Sample::Counter; sub new { my $class = shift; my $ob = (shift || 0); bless \$ob, $class; } sub count { my $self = shift; $$self += (shift || 1); } sub read { my $self = shift; return $$self; }

    Used like so:

    my $counter = new Acme::Sample::Counter (); $counter->count; # add 1 $counter->count(10); # add 10 say $counter->read;

    Note however, that $counter->count(0) adds 1! Think about why this is so.

Re: How to access a variable inside subroutine?
by perlfan (Vicar) on Sep 05, 2020 at 20:12 UTC
    As an alternative to the state keyword for turning your subroutine into a coroutine (a function that maintains state between calls), here is how you use a closure to effectively maintain a state variable declared outside of the counter subroutine and provides access to the current value of $add via subroutine "getter". In this way, $add is read only and only affected via counter. The ADD_COUNTER_CLOSURE: is simply a label handy in this case for self documenting the existence of the closure itself.
    ADD_COUNTER_CLOSURE: { # $add is not accessible outside of closure, but # you do the 'getter', "add" which has access to $add my $add = 0; sub counter { my @nums = (1..500); for my $num(@nums) { $add += $num } } # getter sub add { return $add; } } # $add is not accessible directly here, must use add subroutine my $add = add; print qq{$add\n}; # increment $add via closure() counter(); $add = add; print qq{$add\n};

    Output:

    0 125250
      I had an idea when you used the pre-state closure idea: put your getter inside the context of counter, so it that $add is in scope:

      use 5.030; use strict; use warnings; sub counter { my @nums = (1..500); state $add = 0; for my $num (@nums) { $add += $num; } sub getter { $add } # because this sub is inside the scope of coun +ter(), it has visibility on $add! } counter(); say "getter => ", getter();

        Side note: be careful doing this with a my variable because some weird things can happen with nested subs referencing lexicals from the enclosing scope. Look for Variable "%s" will not stay shared in perldiag.

        The cake is a lie.
        The cake is a lie.
        The cake is a lie.

        Hi,

        Thank you!! It worked!!....I have always been baffled by the "getter/setter" stuff...I still don't understand it, but still was able to run the code you wrote.

        Very nice, thank you. I was going old school but now this is a lot cleaner and more concise.
Re: How to access a variable inside subroutine?
by pritesh_ugrankar (Monk) on Sep 05, 2020 at 21:10 UTC

    Respected Monks,

    Thank you for your replies. I tried the last one suggested by pryrt and it worked!!

    Truly thankful that so many of you took time to reply. The perlmonks is truly a great forum.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others pondering the Monastery: (9)
As of 2021-01-25 14:24 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    Notices?