PERL5.26.1 eval tr operator with interpolation (example on p 76 of camel book) does not work

Viewed 93

trying to use a variable as a pattern in a tr operator statement. The camel book p.76 says to use eval like so:

eval "tr/$oldlist/$newlist/";

Using perl_5.26.1 on Linux there are two things wrong with that: a) using quotes as above eval gives an error message. So you must put it in a block using braces. b) even in braces it does not work. What am I doing wrong? here is the code and ouput:

    $kards = -99;
    $bad="AKQJT98765432KKKK";
    $card='K';
    eval {print "eval is testing for $card in $bad \n"; 
           $kards = $bad =~ tr/$card//; # tried also tr/$card/$card/; same result.
          };
    print "Num of $card in $bad = $kards \n";
    $king = $bad =~ tr/K//;
    print "Num of K in $bad is = $king \n";
=cut    
    Returns the following:
eval is testing for K in AKQJT98765432KKKK 
Num of K in AKQJT98765432KKKK = 0 
Num of K in AKQJT98765432KKKK is = 5 
    
=end 
1 Answers

You want to remove a set of characters from a string via tr where that set is determined at runtime, and get how many of those characters were removed?

Since tr doesn't do variable interpolation as noted in the documentation and the Programming Perl book you're looking at, yes, you have to use eval with a string argument:

#!/usr/bin/env perl
use warnings;
use strict;
use feature qw/say/;

my $cards = "K";
my $str = "A12K34567890JQK";
my $count = eval "\$str =~ tr/$cards//";
say $count;

will produce 2 (With no warnings on 5.22 or 5.32; don't have 5.26 installed anywhere to confirm with it). Note having to escape the $ in the variable the tr operates on in the string so it isn't interpolated first.


You can do this without the potentially-dangerous eval, though, using s//:

my $count = $str =~ s/[\Q$cards\E]//g;
Related