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

I have a database table with simple text values in it for given keys. I'm tie-ing that table using Tie::RDBMS. So I have a %dbhash containing these values. I have another table that contains what I'll call perl-strings. That is, they are strings, but could look like "Hello, $dbhash{name}, We're sending you your new $dbhash{prize}.". I've tied this table to a hash using Tie::RDBMS as well, so let's say %to_interpolate holds this information. I'm looking for the most efficient way to basically do:
my %interpolated = (); foreach (keys %to_interpolate) { $interpolated{$_} = eval "qq/$to_interpolate{$_}/;"; }
I know, however, that eval on strings is relatively slow, so I'm wondering if there's a more efficient way to do this. I really don't need all the power of eval as I'm just trying to interpolate variables, but I haven't been able to do this by other means.

Replies are listed 'Best First'.
Re: More Efficient Than Eval
by revdiablo (Prior) on Aug 19, 2005 at 00:48 UTC

    An idea that might or might not work, depending on how similar and consistent your "perl-strings" are:

    my %dbhash = ( name => 'revdiablo', prize => 'pony', ); my $template = 'Hello $dbhash{name}, you won a $dbhash{prize}.'; #$template =~ s/\$dbhash{([^}]+)}/$dbhash{$1}/ge; # unnecessary $template =~ s/\$dbhash{([^}]+)}/$dbhash{$1}/g; print "$template\n";

    Update: of course, you might notice that this too uses eval. It just does it in a more constrained manner. I don't know how the difference in performance will work out, but I think it's much less dangerous in any event.

    Another Update: aye, I just realized the /e is unnecessary, as the replacement part of a s/// is already interpolated as a double-quoted string. It can simply be s/\$dbhash{([^}]+)}/$dbhash{$1}/g.

      you might notice that this too uses eval.
      Actually, /e doesn't do an eval - the code is compiled at the same time as the main body of code; you need /ee for that. If $& was an lvalue, then the following pairs would be equivalent:
      s/foo/bar/ s/foo/bar/e s/foo/bar/ee $& = "bar" $& = bar $& = eval bar

      Dave.

Re: More Efficient Than Eval
by Roy Johnson (Monsignor) on Aug 19, 2005 at 00:20 UTC
    You're pretty much stuck with eval, since you want to interpolate what would be symrefs inside a string. You can type a little less by doing
    $_ = eval qq("$_") for values %to_interpolate;

    Caution: Contents may have been coded under pressure.
Re: More Efficient Than Eval
by xdg (Monsignor) on Aug 19, 2005 at 11:24 UTC

    If all you are doing is putting variables into the strings -- assuming that you are creating the strings also -- just put a marker in the string and replace it with a regex substitution. Instead of writing "Hello $dbhash{name}, you have...", just write "Hello NAME, you have..." and do

    foreach $string_to_fill_in (@strings) { $string_to_fill_in =~ s/\bNAME\b/$dbhash{name}/g; # repeat for as many parameters as you have }

    If you need more complex behavior, consider storing a template string from one of the major templating modules (e.g. HTML::Template) and use the template module to fill in your data.

    -xdg

    Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.