Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Re^3: why lexical variables can not be interpolated?

by kennethk (Abbot)
on Oct 21, 2013 at 17:16 UTC ( #1059147=note: print w/ replies, xml ) Need Help??


in reply to Re^2: why lexical variables can not be interpolated?
in thread why lexical variables can not be interpolated?

As described in s/PATTERN/REPLACEMENT/msixpodualgcer in perlop,

e Evaluate the right side as an expression.
ee Evaluate the right side as a string then eval the result.
The reason $text =~ s/(\$\w+)/$1/eeg; works is the regular expression stores $AGE (string literal) in $1. For the substitution, $1 is evaluated to return $AGE, and then $AGE is evaluated to return 17. Your second code attempts to multiply the string literal $AGE by 2, not the value of it - you've got your order of operations off. You could accomplish this using an explicit dereference rather than invoking eval twice, like
$text =~ s/\$(\w+)/$$1 * 2/egx; #I tried to double the age and succee +ded!
What I've changed:
  1. I used an explicit dereference $$1, which could also be expressed as ${$1}
  2. I changed the pattern so that the sigil is replaced, but is not part of the pattern. This is necessary because $$1 attempts to access the scalar variable named AGE; otherwise it would assume you are looking for a variable with an explicit dollar sign in its name.
  3. I changed the code to eval only once.

I realize that this is a learning exercise, but for reference if I were going to do this kind of templating, I would use sprintf in something like:

$AGE = 17; $tmpl = 'I am %s years old'; # note single quotes $text = sprintf $tmpl, $AGE * 2; # print I am 17 years old

#11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.


Comment on Re^3: why lexical variables can not be interpolated?
Select or Download Code
Re^4: why lexical variables can not be interpolated?
by lightoverhead (Monk) on Oct 21, 2013 at 17:42 UTC

    kennethk

    Thank you for your detailed answers to this question.

    I know if I use a symbolic reference will get this done. But it will not work for lexical scope variable. If, for example, I use:

    my $AGE =17 #instead of $AGE=17

    your solution will fail.

    It seems to me that it's impossible to use /e to solve this situation when variable is lexical scoped.

    Yes, sprintf will work for this case as we know it's a number; but in other cases we may not know if $AGE holds a numeric value.

    Any thoughts?

    Thank you!

      It seems to me that it's impossible to use /e to solve this situation when variable is lexical scoped.
      There's no way around that using symbolic variables because symbolic variables only access package variables. The easiest drop in would be using a hash, which might look like:
      my %hash = (AGE => 17, ); $text =~ s/\$(\w+)/$hash{$1}/g;

      This has the advantage of removing a bunch of misdirection and potential security complications. If you wanted to make key misses fatal, you can use lock_hash in Hash::Util; of course, your symbolic reference code doesn't die on misses.

      Yes, sprintf will work for this case as we know it's a number; but in other cases we may not know if $AGE holds a numeric value.

      sprintf works for any string. Note I use %s for string replacement; numerical replacement would be %d. Obviously the multiplication only works if $AGE is a number, but that's sort of given.

      If you are learning this with an eye toward production code, please read Why it's stupid to use a variable as a variable name. Also, there are a large number of real templating packages out there, such as HTML::Template for simple projects and Template::Toolkit for more complex ones.


      #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

        Thanks hash will work! and Thank you for your recommendations too.

      use strict; use warnings; my %legalReplacements = (name=>'Joe', age=>42); my $text = q(Hi I'm $name and happen to be $age. You can call me $nam +e, if you tell me your $ARGV.); my $regex = qr/\$([a-zA-Z]+)/; while ($text =~ /$regex/) { if (not exists $legalReplacements{$1}) { warn "INTRUDER ALERT: attempting access to '$1'"; last; } $text =~ s/$regex/$legalReplacements{$1}/; } print "Final text:\n"; print $text;
      gives
      C:\>perl Test.pl INTRUDER ALERT: attempting access to 'ARGV' at test.pl line 13. Final text: Hi I'm Joe and happen to be 42. You can call me Joe, if you tell me y +our $ARGV. C:\>_

        Nice solution! Hash is used here too. Thank you.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others exploiting the Monastery: (12)
As of 2015-07-02 08:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (31 votes), past polls