Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Referencing the locals

by Chuma (Scribe)
on Apr 28, 2021 at 18:16 UTC ( [id://11131804]=perlquestion: print w/replies, xml ) Need Help??

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

Hello monks!

I'm having some trouble with referencing local variables. Simplified example:

$a=3; { my $a=5; say ${a}; say ${'a'}; }

Prints:

5 3

What is this madness, and how do I make it stop?

The typical case where I want to use it is when I have a handful of arrays and want to do stuff with each of them, like so:

@cat=(1,2,3); @dog=(5,6); @pig=(4,3,2,1); for('cat','dog','pig'){ stuff(@$_); }

which works fine as long as the arrays are global, but if they're local, it doesn't work. How can I reference the local ones?

Replies are listed 'Best First'.
Re: Referencing the locals
by davido (Cardinal) on Apr 28, 2021 at 19:18 UTC

    Don't wield symbolic references for this purpose. You don't need them here. Use strict to disabuse yourself of any temptation. Why it's stupid to use a variable as a variable name.

    In your code above, ${'a'} is a symbolic reference to a package global named $main::a. And ${a} is synonymous with $a, which in the scope you're using it in, refers to the lexical version of $a, not the package global.

    Instead, either use real references, or a hash:

    # Using hard references my @cat = (1,2,3); my @dog = (5,6); my @pig = (4,3,2,1); for my $array (\@cat, \@dog, \@pig) { # On first iteration $array will contain a reference to @cat. # On second iteration, $array will contain a reference to @dog. # On third iteration $array will contain a reference to @pig. }

    ...or do this...

    # Using a hash my %animals = ( cat => [1,2,3], dog => [5,6], pig => [4,3,2,1], ); for my $animal_type (keys %animals) { my @values = @{$animals{$animal_type}}; # Animals will come in an unpredictable order, but as an example: # First iteration, dog will be handled. # Second iteration, pig will be handled. # Third iteration, cat will be handled. }

    Dave

Re: Referencing the locals
by haukex (Archbishop) on Apr 28, 2021 at 19:12 UTC
      ${'a'} is a symbolic reference. The latter is forbidden under strict, and that's a good thing, as it can easily cause all kinds of hard-to-debug chaos

      Perhaps a silly question but I don't know the answer so I shall ask it anyway...
      Is there ever a case to use a symbolic reference in Perl? The documentation doesn't give any hint as to why one would be needed other than to describe them as powerful.

        Is there ever a case to use a symbolic reference in Perl?

        Two places that come to mind where I regularly write no strict 'refs'; is dynamic method/function generation (example 1, example 2) and implementations of import (example 1, example 2, at the bottom of each node). Other than those legitimate uses, there's interaction with legacy code (example 1, example 2). Note how in each of those examples the scope of the no strict is limited to be as small as possible.

        Update: Added more links to examples.

        Historically, they were probably useful, but I came to Perl when 5.6 was already there, so I don't remember them. Nowadays, strict forbids them, so the answer is "No". On the contrary, check Can you use string as a HASH ref while "strict refs" in use? on how confusing they can be.

        map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: Referencing the locals
by 1nickt (Canon) on Apr 28, 2021 at 18:24 UTC

    Hi. First, use strict and warnings and see what happens, then never turn them off again.

    Second, don't use "a" or "b" as variable names. They are special.

    For your stated objective:

    my @cat=(1,2,3); my @dog=(5,6); my @pig=(4,3,2,1); for(\@cat,\@dog,\@pig){ stuff($_); # stuff() gets an arrayref and will need to dereference + it }

    Hope this helps!


    The way forward always starts with a minimal test.
Re: Referencing the locals
by AnomalousMonk (Archbishop) on Apr 28, 2021 at 23:52 UTC

    As an example of package-global versus lexical naming rules and also the scoping effects touched on elsewhere in this thread, consider:

    Win8 Strawberry 5.8.9.5 (32) Wed 04/28/2021 19:35:55 C:\@Work\Perl\monks >perl -l use strict; use warnings; our $foo = 'package global'; { # begin scope my $foo = 'lexical'; print 'A: ', $foo; print 'B: ', $::foo; print 'C: ', $main::foo; } # end scope ^Z A: lexical B: package global C: package global
    Now try the code without the { ... } scope. Is there any difference? If so, why? Is there any way to do without the our declaration in the
        our $foo = 'package global';
    assignment statement? Is there any reason to avoid our? See also perldata.


    Give a man a fish:  <%-{-{-{-<

Re: Referencing the locals
by hrcerq (Scribe) on Apr 28, 2021 at 18:50 UTC

    Hi. You'll get a better understanding of this by taking a look on Variables and Scoping tutorials, and I definitely recommend this article too.

    P.S.: Later on, I noticed scoping rules are not the key point here. Yet the reading I mentioned helps.

    return on_success() or die;

      You'll get a better understanding of this by taking a look on Variables and Scoping tutorials, and I definitely recommend this article too.

      Note that this isn't an issue of scoping, and none of those links discuss symbolic references.

      $ perl -le '$x=2; my $x=4; print ${x}, ${"x"}' 42

        My bad. It's just that I saw the term local variables associated to lexical variables, and ended up focusing on that. I'm sorry.

        return on_success() or die;

Re: Referencing the locals
by Chuma (Scribe) on Apr 29, 2021 at 00:30 UTC

    Thank you all for your answers!

    It's fair to say this is all firmly in the chaotic evil school of programming, so it's no wonder "strict" doesn't like it.

    Using explicit references would work in this example, but then I can't do anything else with the string. A hash would allow that, although I probably don't want to refer to the array as @{$animals{'cat'}} for the rest of the program, so I guess I could add

    my @cat = @{$animals{'cat'}}; my @dog = @{$animals{'dog'}}; my @pig = @{$animals{'pig'}};

    There's also the option of just repeating the whole code block three times over instead of looping. I suppose that's alright, but doesn't really strike me as beautiful code.

    The text about "why it's stupid to use a variable as a variable name" makes a good point, generally. Symbolic references can lead to uncontrolled global variables – if $x turns out to be something unexpected, there's no telling what $$x might destroy. But that's not really applicable here, firstly because the variable takes on exactly these three values, and secondly because I'm trying to make the variables not global. It seems like an odd design choice that that's apparently impossible, but maybe it's just an implementation artefact.

      although I probably don't want to refer to the array as @{$animals{'cat'}} for the rest of the program, so I guess I could add
      my @cat = @{$animals{'cat'}}; my @dog = @{$animals{'dog'}}; my @pig = @{$animals{'pig'}};

      Note that makes a shallow copy of the array - personally I'd write my $cat = $animals{cat};, and if it's just a simple array and not a nested structure I'd write $$cat[$i] to access it (though many people also prefer the $cat->[$i] syntax).

      Note that as of Perl 5.24, you can write $animals{cat}->@* instead of @{$animals{cat}} (in 5.20 and 5.22 this needed a use feature 'postderef';). Also, as of 5.22 there's the still experimental Assigning to References, where you can say \my @cat = $animals{cat}; and have @cat be an alias to the arrayref, which IMHO is really nice, but the feature is still experimental so it is subject to change!

      Update: A few more comments:

      There's also the option of just repeating the whole code block three times over instead of looping. I suppose that's alright, but doesn't really strike me as beautiful code.

      I agree, DRY applies.

      The text about "why it's stupid to use a variable as a variable name" makes a good point, generally. ... But that's not really applicable here

      I think the point is more to make a case for why strict is a good thing.

        Huh, I didn't know that, about the shallow copy. But in this case, since I'm just trying to "rename" the array, shallow copy seems like the right thing.

        There are of course situations where you can't avoid repetition, but I always figured this was a neat shortcut in Perl (I can't think of any other language that would allow it). I often find that I want to, for example, open cats.txt, @cats=<$fh>, $type{$_}='cats', print "there were $cats cats", $max{'cats'}=max(@cats), or whatever the case may be – and then do the same thing for 'dogs'. I'm not aware of any other way that's as succinct and simple as looping over the strings.

        There are a lot of people who argue that you should always use "strict", basically changing the language into a stricter version of itself. There are also those who argue that you should use an even more strict version of Perl, and that it's called Python. Different preferences, I guess.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (3)
As of 2024-04-19 20:04 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found