http://www.perlmonks.org?node_id=15326

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

Seriously, last night I had a dream where I did the following:
my $text = q{ Dear $person, I know that this text is $adjective. But I wish it could be... }; my $person = 'Mom'; my $adjective = 'not interpolated'; DoSomethingToInterpolate($text);
and the result turned out to be (obviously)
Dear Mom, I know that this text is not interpolated. But I wish it could be...
So, is this just a dream or is there a way to do this? I see this as a two parted problem:...this sounds more like a meditation node.

Replies are listed 'Best First'.
Re: Dreaming of Post Interpolation
by mdillon (Priest) on May 30, 2000 at 07:08 UTC
    check out this Interpolation module from M-J. Dominus:
    http://www.plover.com/~mjd/perl/Interpolation/

    i haven't checked it out myself, but it looks pretty interesting from just looking at the web page.

    i don't think that there is a simple, transparent syntax or function for doing what you want in Perl. at the least, you can check out the techniques MJD uses in his Interpolation module and go from there.

    most of the time, i use (s)printf for such things, but it does not have the nice feature of explicitly tying the name to the postition as does symbolic variable interpolation (i.e. it's hard to tell which '%s' goes with which variable when there are twenty of them; 'Hello $Recipient' is transparent).

      Dude, congratulations! That was exactly what I was looking for! I wonder why it hasn't been introduced into the core! I can think of 2031 different ways of using the Interpolation.pm (and the name is so damn obvious, I don't know how I never ran across it on CPAN). I'm downloading it now to play with it a bit and see if I can get the desired results from it.

      Thank you, thank you!!

      #!/home/bbq/bin/perl
      # Trust no1!
        i poked around CPAN a bit looking for it, but aside from a few stale references to it's being on CPAN in Google, i couldn't find it there.
Re: Dreaming of Post Interpolation
by lhoward (Vicar) on May 30, 2000 at 05:15 UTC
    Here's my stab at it. By storing the original text in a function instead of a scalar I can delay interpolation of the $vars until call time. See this example:
    $text = sub { <<EOT; Dear $person, I know that this text is $adjective. But I wish it could be... EOT }; local $person = 'Mom'; local $adjective = 'not interpolated'; print &$text;
    I don't think this is quite what you were looking for, but I think it is along the right path. I have a feeling that something with eval might do the trick too, but I can't figure out how to get that to work right now.
      Hey! That's very close to the general idea! But it doesn't deal with my previous post with numbers... The delaying part is attractive, but unfortunately you only get to do one assignment. I would like to have several variables (and variables composed from those) affected by change of one.

      The best reference that pops into mind (and the simplest one) is Excel. Kinda like having an entire column defined from a cell, then cascading down the line when you change the value of that cell.

      But, delaying works admirably when you're dealing with a single variable. It would just be a pain in the behind to have to do that for each (no pun intended) one of them all.
        Unless I misunderstand what you're asking for; you can do the assignment multiple times with different values:
        $text = sub { <<EOT; Dear $person, I know that this text is $adjective. But I wish it could be... EOT }; my @subs=('Mom:not interpolates', 'Dad:maybe interpolated', 'Sis:who knows'); foreach (@subs){ local ($person,$adjevtive)=split ':',$_; print &$text; }
        My technique will also work for your number example (with everything stored as a function). However it look slightly ugly:
        $n = sub {1}; $m = sub {&$n*2}; $o = sub {&$m*2}; $s = sub {&$n." ".&$m." ".&$o."\n"}; print &$s; $n = sub {2}; print &$s;
Re: Dreaming of Post Interpolation
by Anonymous Monk on May 30, 2000 at 20:49 UTC
    my $text = q{ Dear $person, I know that this text is $adjective. But I wish it could be... }; my $person = 'Mom'; my $adjective = 'not interpolated'; print eval "qq{$text}";
      YES!

      this is the cleanest i've seen yet. the only thing that breaks this is unbalanced brackets, but in a 'sub interpolate', it's easy enough to get around such restrictions.

      on a side note, the Interpolate module doesn't really help much for BBQ's problem. this solution, however, gets perl to do real double-quotish interpolation on an arbitrary scalar value.

        Yeah, the Interpolat module wasn't as good as what it looked like from the examples. It just does a few tricks with hashes, and seems to be directed towards maintainin string formats more than actually doing heavy linkage.

        I think that what I was looking for is a new data state. Something that would cascade through whatever assignments had been done before. My numeric example was probably the best way to demonstrate. Come to think of it, cascade sounds like a cool name. Imagine it:
        cascade $n = 1; my $m = $n*2; my $o = $m*2; my $s = "$n $m $o\n"; print $s; $n = 2; print $s; ^d 1 2 4 2 4 8
        now would THAT be cool? Does anyone know if there a perl ideas/wishlist somewhere?

        #!/home/bbq/bin/perl
        # Trust no1!
Re: Dreaming of Post Interpolation
by takshaka (Friar) on May 30, 2000 at 04:02 UTC
    Eh? That's just filling in a template. You can do it with symbolic refs (if the variables aren't lexicals) or, preferably, with hash lookups.
    my $text = q{ Dear $person, I know that this text is $adjective. But I wish it could be... }; my %replace = (person => 'Mom', adjective => 'not interpolated'); $text =~ s/\$(\w+)/$replace{$1}/g; print $text;
      Yes, regex... But would that be considered true interpolation?

      The idea of searching within word boudaries for a string starting with \$ occured to me several times, but I have always thought of it as not being very practical. Let me put it this way (this time with math):
      $n = 1; $m = $n*2; $o = $m*2; $s = "$n $m $o\n"; print $s; $n = 2; print $s; ^d 1 2 4 2 4 8
      Sure this is a simplistic example (as was my previous), but what when you have 1000+ more lines of code, wouldn't it be alot easier to control? Is the only way out to run a regex after assignment? There must be another way... Think of the possibilities!

      Am I suffering from fuzzy-logic syndrome?
        I forgot about the double-e substitution ( ie, s/(\$\w+)/$1/gee ). You can do that with lexicals.

        No matter what, you end up doing some sort of substitution and/or eval. In the second example you want to re-evaluate $m=$n*2 whenever $n is reassigned, which isn't possible without creating more complex data structures. That's far different from interpolating variables into a string.

Re: Dreaming of Post Interpolation
by xeh007 (Sexton) on May 30, 2000 at 05:23 UTC
    OK, here we go...
    eval '$interpolated = "' . $text . '"';
    $interpolated should now be the interpolated version of $text.

    Yes, I know this is a cheap way of doing it, but it does work... (It even catches references, hashes, arrays, etc.!)

Re: Dreaming of Post Interpolation
by Corion (Patriarch) on May 30, 2000 at 15:51 UTC
    In that code piece of mine I use the awkward version of interpolation , that is, running a regex over the result until no more matches for (in my case) "$\(\w+\)" are found and I replace the stuff from a hash instead of the global namespace - for security reasons, I find it more appealing to use a dedicated hash instead of global namespace, but that's just my opinion ;D
Re: Dreaming of Post Interpolation
by takshaka (Friar) on May 30, 2000 at 23:43 UTC
    Here's the module I was working on last night before you found Interpolation.pm. It's harsh, but actually gets the job done, although you have to type your variables as either string or expression. I like the way Interpolation.pm uses import() instead of forcing you to tie your variables yourself, so I borrowed that bit.

    Update: Having had a few moments to mull over code generated too late at night, I see it is simpler than I was making it--none of that silly data typing is needed....

    package EvalScalar; use Carp; sub import { my $caller_pkg = caller; my $my_pkg = shift; for (@_) { my $var = $_; $var =~ s/^\$//; my $temp; tie $temp, $my_pkg; *{$caller_pkg . '::' . $var} = \$temp; } } sub unimport { my $caller_pkg = caller; my $my_pkg = shift; for (@_) { my $var = $_; $var =~ s/^\$//; my $temp; my $old_var = *{$caller_pkg . '::' . $var}{SCALAR}; *{$caller_pkg . '::' . $var} = \$temp; untie $$old_var; } } sub TIESCALAR { my $pkg = shift; my $type = shift; my $temp = ''; my $self = \$temp; bless $self, $pkg; return $self; } sub STORE { my $self = shift; my $value = shift; $value =~ s/\$(\w+)(?!::)/'$' . caller() . '::' . $1/ge; $$self = $value; } sub FETCH { my $self = shift; my $value = eval $$self; if ($@) { carp $@; return ''; } else { return $value; } } 1;
    and the example usage...
    #!/usr/bin/perl -w use strict; use EvalScalar qw($a $b $c); # Only works with globals use vars qw($person $num); $num = 3; # Single-quote the rhs if you want # evaluation of the assignment # delayed until read-time. $a = '$num * 2'; $b = '$a + $num'; $c = '"$num, $a, $b"'; for $num (1..5) { print "\$a=$a, \$b=$b, \$c=$c\n"; } $a = '"Dear $person, this sorta works\n"'; for $person qw(Mom Dad BBQ) { print $a; } $a = '"But unqualified subs will be called in EvalScalar namespace" . +foo()'; print $a;
Re: Dreaming of Post Interpolation
by chromatic (Archbishop) on Jun 06, 2000 at 22:16 UTC
RE: Dreaming of Post Interpolation
by Anonymous Monk on May 30, 2000 at 22:52 UTC
    How 'bout this:
    my $text = 'Dear $person, I know that this text is $adjective.
    But I wish it could be...';
    
    my $person = "foobar";
    my $adjective = "flooby";
    
    $interpolated_text = eval($text);
    
      put this afterward and see what happens: if ($@) { print "$@\n"; } syntax error at (eval 1) line 3, at EOF
Re: Dreaming of Post Interpolation
by Aighearach (Initiate) on May 30, 2000 at 05:19 UTC
    > interpolating single-quoted variables

    Isn't this just a case of, in your dream-state, using single quotes where in reality you would have used double quotes?

    Maybe I am not understanding the problem, but it seems like the sollution would either be reinventing single quotes, or eval

    Paris Sinclair    |    4a75737420416e6f74686572
    pariss@efn.org    |    205065726c204861636b6572
    I wear my Geek Code on my finger.
    
      Yes, I guess the single-quote part was a bit of a slip-up on my part. There is no reason for the desired effect that I am looking for to be acheived exclusively with single-quotes. As a matter of fact, double-quotes would be even better since they represent (and do) interpolate.

      My mess-up was based on the fact that I wanted a string to be re-assigned when it doesn't do it automatically. The single-quote part was my goof, and a bit of an exageration. Now I can't find any good reason why they couldn't be double in the first place. Thanks for pointing that out. Its important.
A reply falls below the community's threshold of quality. You may see it by logging in.