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

Thank you for replying to my questions.

After playing around with this '/e' modifier with s///, I do have a question which I cannot solve now:

$AGE = 17; $text = 'I am $AGE years old'; # note single quotes $text =~ s/(\$\w+)/$1/eeg; # print I am 17 years old $text =~ s/(\$\w+)/$1 * 2/eegx; #I tried to double the age, but faile +d

How I can double the age in s/// format for the above code as showed by a similar but different example:

$text = "I am 17 years old"; $text =~ s/(\d+)/2 * $1/eg; #now I am 34 years old

Thank you.

Replies are listed 'Best First'.
Re^3: why lexical variables can not be interpolated?
by kennethk (Abbot) on Oct 21, 2013 at 17:16 UTC
    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.

      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!

        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:\>_
        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.