Using the char variable in tr ///

I am trying to count the characters in a string and found a simple solution counting a single character with the tr operator. Now I want to do this with every character from a to z. The following solution does not work, because tr/// corresponds to each character.

 my @chars = ('a' .. 'z'); foreach my $c (@chars) { $count{$c} = ($text =~ tr/$c//); } 

How to use char variable in tr/// correctly?

+5
source share
5 answers

tr does not support variable interpolation (neither in the search list nor in the replacement list). If you want to use variables, you should use eval() :

 $count{$c} = eval "\$text =~ tr/$c/$c/"; 

However, a more efficient (and safe) approach would be to simply iterate over the characters in the string and increment counts for each character, for example:

 my %count = map { $_ => 0 } 'a' .. 'z'; for my $char (split //, $text) { $count{$char}++ if defined $count{$char}; } 
+5
source

tr/// does not work with variables unless you wrap it in eval

But there is a better way to do this:

 $count{$_} = () = $text =~ /$_/g for 'a' .. 'z'; 

For TIMTOWTDI:

 $count{$_}++ for grep /[az]/i, split //, $text; 
+7
source

If you look at perldoc for tr/SEARCHLIST/REPLACEMENTLIST/cdsr , you will see the following at the bottom of the section:

Since the transliteration table is built at compile time, neither SEARCHLIST nor REPLACEMENTLIST are double-quoted. This means that if you want to use variables, you must use eval ():

 eval "tr/$oldlist/$newlist/"; die $@ if $@ ; eval "tr/$oldlist/$newlist/, 1" or die $@ ; 

So you need eval to create a new SEARCHLIST.

It will be very inefficient ... the code may seem neat, but you process the full line 26 times. You also do not count uppercase characters.

You'd better go over the line once and just increment the counts for each character found.

+3
source

From the perlop documentation :

 tr/AAA/XYZ/ 

transliterates any AX.

Because the transliteration table is built at compile time, neither the SEARCHLIST or REPLACEMENTLIST list is double-quoted interpolating. This means that if you want to use variables, you must use eval ()

Alternatively, in your case, you can use the s/// operator as:

 foreach my $c (@chars) { $count{$c} += ($text =~ s/$c//g); } 
+1
source

My solution with some modification based on http://www.perlmonks.org/?node_id=446003

 sub lowerLetters { my $string = shift; my %table; @table{split //, $letters_uc} = split //, $letters_lc; my $table_re = join '|', map { quotemeta } reverse sort keys %table; $string =~ s/($table_re)/$table{$1}/g; return if not defined $string; return $string; } 
+1
source

All Articles