Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

Using variables with tr///

by greenhorn (Sexton)
on Jul 17, 2000 at 10:39 UTC ( #22826=perlquestion: print w/replies, xml ) Need Help??

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

I had it in mind to pass a string and a character to a subroutine, and within the subroutine, use a variable ("$char", below) containing the character as follows:

<kbd>$count = $string =~ tr/$char/$char/;</kbd>

Purpose: get a count of occurrences of "$char" within "$string".

In which case, tr/// happily counts occurrences of "c" and "h" and "a" and "r"...just as I told it to do. :)  Can a variable be used in this instance? I realize that s/// can be used; it will be less efficient than tr///--so I understand--but it will return a count, given the right syntax.

Slightly off-topic: in perlop (ActiveState's, at least), the following appears in the section on tr///:
   Options:
      c    Complement the SEARCHLIST.

Just below that, the following appears:
   If the /c modifier is specified, the SEARCHLIST character set is complemented.

Aspiring technical writers everywhere, please study the above for a moment for an example of how not to write documentation--whether it's end-user documentation, or "only" reference material.</rant>

Replies are listed 'Best First'.
Re: Using variables with tr///
by plaid (Chaplain) on Jul 17, 2000 at 10:57 UTC
    Also from perlop:
    Note that because the transliteration table is built at compile time, neither the SEARCHLIST nor the REPLACEMENTLIST are subjected to double quote interpolation. That means that if you want to use variables, you must use an eval(): eval "tr/$oldlist/$newlist/"; die $@ if $@; eval "tr/$oldlist/$newlist/, 1" or die $@;

      This is significantly faster than using eval:

      sub count { my ($string, $char) = @_; my $index = -1; my $count = -1; do { $count++; $index = index($string, $char, $index + 1); } while ($index != -1); return $count; }

      And this is faster still:

      sub count { my ($string, $char) = @_; $string =~ s/[^$char]//g; return length($string); }
      Eval: 56 wallclock secs (45.19 usr + 0.00 sys = 45.19 CPU) @ +3319.32/s (n=150000) While Loop: 5 wallclock secs ( 4.69 usr + 0.00 sys = 4.69 CPU) @ +31982.94/s (n=150000) Substitution: 1 wallclock secs ( 1.41 usr + 0.00 sys = 1.41 CPU) @ +106382.98/s (n=150000)

      Update (7/19/00): Oh, duh. s/// returns a count of the number of changes made. Here's a faster way to write the subroutine:

      sub count { my ($string, $char) = @_; return ($string =~ s/$char//g); }

      - Matt

      It does sound as if s/// might end up being as efficient (perhaps unless the string is quite long...subject for further tests). Now I see that the question stems in part from an RTFM problem, shame on me. Perhaps I will vote against my own message. Thanks, plaid and btrott.

      This also means by implication that $_ must be used:
      eval("$c= $d =~ tr/$e/$f/")
      Opens a whole bag of bunnies, I think you'll agree.
      I've had to use:  eval "tr/$a/$b/" instead.

      --

      Brother Frankus.

      ¤

Re: Using variables with tr///
by btrott (Parson) on Jul 17, 2000 at 10:58 UTC
    The searchlist to tr is generated at compile-time, so you can't use variables and expect them to be interpolated at run-time.

    In the docs:

    Note that because the translation table is built at compile time, neither the SEARCHLIST nor the REPLACEMENTLIST are subjected to double quote interpolation. That means that if you want to use variables, you must use an eval(): eval "tr/$oldlist/$newlist/"; die $@ if $@; eval "tr/$oldlist/$newlist/, 1" or die $@;
    So you'll have to use eval for that. In which case, you may get better performance using s/// anyway; you'll have to benchmark it to check.
      And that benchmark, quite pitifully, looks like this:
      Benchmark: timing 1000000 iterations of Eval, S_op... Eval: 140 wallclock secs (133.80 usr + 0.03 sys = 133.83 CPU) @ + 7472.17/s (n=1000000) S_op: 1 wallclock secs ( 0.81 usr + 0.00 sys = 0.81 CPU) @ 12 +34567.90/s (n=1000000)
      with:
      #!/usr/bin/perl -w use strict; $|++; use Benchmark; my $string = 'cat'; my $search = 'c'; my $replace = 'b'; timethese( 1000000, { Eval => sub { eval "$string =~ tr/$search/$replace/" + }, S_op => sub { $string =~ s/$search/$replace/g }, } );
      Man, that's ugly.
      --
      Casey
      

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://22826]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others wandering the Monastery: (4)
As of 2022-10-06 20:23 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My preferred way to holiday/vacation is:











    Results (28 votes). Check out past polls.

    Notices?