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

How do I perform the following:     $var =~ tr/$pattern1/$pattern2/; where both $pattern1 and $pattern2 are variables.

The Perl on-line maunual says I must use eval() to include variables in a 'tr' expression, but I don't understand how to do that.

Edit by dws for formatting, meaningful title

Replies are listed 'Best First'.
Re: Transliteration!!!
by demerphq (Chancellor) on Oct 20, 2002 at 20:26 UTC
    There may be a better way, but when ive needed to do this ive used something like:
    use strict; use warnings; sub make_tr { my $from=quotemeta(shift); my $to =quotemeta(shift); (my $opts=shift || "")=~tr/cdsCDS/cdscds/d; my $tr=eval "sub { (defined \$_[0] ? \$_[0] : \$_)=~tr/$from/$ +to/$opts}"; die "$@" if $@; return $tr; } my $tr=make_tr('A-Z','a-z'); my $x="YaBaDaBaDo"; $tr->($x); print $x,"\n"; $_="FOO"; $tr->(); print; __END__ yabadabado foo
    Which returns a subroutine that can do the desired translation on the fly. Note that it works on $_ if a parameter isnt supplied, and returns the same thing that tr/// would. Itll die if the eval fails.

    Good Luck.

    update Forgot the quotemeta() on the first post... And beefed up the $opts cleaning... And added the my()s that are in my local version but not in the original. Sigh.

    --- demerphq
    my friends call me, usually because I'm late....

      That looks nice, but I'd add a couple details if I were to use it:
      sub make_tr { my ($from, $to, $opts) = map defined && quotemeta, @_; $opts = "" unless defined $opts; return eval "sub { (defined \$_[0] ? \$_[0] : \$_) =~ tr/$from/$to/$opt +s }" or die $@; }
      Remember to be very careful with any input to eval EXPR.

      Makeshifts last the longest.

        Heh. Good catch. And to be honest I posted the update before i read your reply... :-) And yours is more idomatic. Cool. :-)

        --- demerphq
        my friends call me, usually because I'm late....

Re: How to eval a tr/// (was: Transliteration!!!)
by graff (Chancellor) on Oct 20, 2002 at 23:52 UTC
    By the way, now that you have the full-featured, maximally advanced, subtle and idiomatic solution for using variables with "tr///", here's a simple, direct answer to your opening question:
    eval "\$var =~ tr/$pattern1/$pattern2/";
    Note the use of double quotes around the expression, as well as the backslash in front of "$var". The double quotes will cause interpolation of variables (variables in the string will be replaced by their values); the backslash causes the "$" in "$var" to be passed as a literal, so "eval" gets a string that begins with the four characters '$var', rather than one that begins with the current value (contents) of the variable "$var".
      Ok, you might have a point there. *blush*

      But there is one reason I didn't show just this. If the same transliteration needs to be applied over and over (such as applied to every element of an array) this could be dreadfully slow. And the idea of an anonymous subroutine is common enough that he might as well learn about em now than later.

      :-)

      --- demerphq
      my friends call me, usually because I'm late....

Re: How to eval a tr/// (was: Transliteration!!!)
by Zaxo (Archbishop) on Oct 20, 2002 at 23:20 UTC

    The idiom meant is:

    eval "\$var =~ tr/$pattern1/$pattern2/;";

    The double quotes allow the patterns to be interpolated before the expression is compiled.

    After Compline,
    Zaxo

Re: How to eval a tr/// (was: Transliteration!!!)
by Anonymous Monk on Oct 21, 2002 at 17:31 UTC
    Thank's for the help. I knew it had to be simple. I had simply forgotten (actually I didn't know any better) the '\' prior to $var.

    I'll work on understanding the anonymous subroutine.