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

Ello, Why doesn't the "tr" work in the latter method? Thanks, Wits End
#!/usr/bin/perl # Perl 5.10.1 built for MSWin32-x86-multi-thread # Count number each @check value occur in $string # Two Methods: Long way (WORKS), Shorter way (PROBLEM) use strict; use warnings; my @check = ("A", "B"); my $string = "2A 3B 4B"; my $count; # how many times does "A" appear $count = $string =~ tr/A//; print "[A]: $count "; # how many times does "B" appear $count = $string =~ tr/B//; print "[B]: $count "; print "\n"; # returns "[A]: 1 [B]: 2" CORRECT # Shorten the above foreach ( @check ) { $count = $string =~ tr/$_//; print "[$_]: $count "; } print "\n"; # returns "[A]: 0 [B]: 0" INCORRECT # What is going on? Why doesn't the "tr" work? exit; # FOOBAR

Replies are listed 'Best First'.
Re: Simple Matching
by gwadej (Chaplain) on Sep 28, 2009 at 04:28 UTC

    From perlop,

    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()

    This obviously applies to tr/$_//.

    G. Wade
Re: Simple Matching
by BrowserUk (Patriarch) on Sep 28, 2009 at 04:44 UTC

    Try

    print "[$_]:", eval "\$string =~ tr/$_//";

    Note: this may be slower than your original.


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thank you both. I was looking at Friedl's book on RegEx and wrapping myself up in eval's when both of you replied. Thank you. I am working on optimizing a bit of code and have not needed eval yet. Is there a workaround for this specific example, e.g., "grep -c"? Counting characters in a string should be fast and simple.

        It's only getting compiled once, just as if it was outside of eval, so what's the problem?

        If you wanted to reuse the counter, make a sub if it:

        $counters{$_} = eval "sub { \$_[0] =~ tr/\Q$_\E// }";
Re: Simple Matching
by ahmad (Hermit) on Sep 28, 2009 at 06:42 UTC

    How about something like this

    sub count { # pass it the long sentence and the match word/char! my ($LONG,$CHAR) = @_; my $count = 0; while ($LONG=~m/$CHAR/) { $LONG=~s/$CHAR//; # remove match? $count++; } return $count; }

    Then you can call it in your code like this

    foreach ( @check ) { print "[$_]: " . count($string,$_); }

    Well, it looks a bit messy, but it does exactly what you need with high performance.

      Ahmad, Thanks. That is not messy and works. Similar to the "Method 1" that I used. The "brute force" method gained a factor of 50 in time on the loop. Your loop (similar to my first go at it) gained a factor of 2. To the mods: I hope I am not getting off track. I have found responses quite helpful. Ugly code can work faster than pretty code, but sometimes perl can be unintelligable, short and extremely fast.
Re: Simple Matching
by johngg (Canon) on Sep 28, 2009 at 11:38 UTC

    How about using a global match?

    $ perl -e ' my @toCheck = qw{ A B }; my $string = q{2A 3B 4B}; foreach ( @toCheck ) { my $count = () = $string =~ m{$_}g; print qq{[$_]: $count }; } print qq{\n};' [A]: 1 [B]: 2 $

    I hope this is useful.

    Cheers,

    JohnGG