Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

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

by kennethk (Monsignor)
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 perusing the Monastery: (10)
As of 2014-08-23 12:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The best computer themed movie is:











    Results (173 votes), past polls