Как я могу считать символы в Perl? - PullRequest
12 голосов
/ 28 сентября 2010

У меня есть следующий скрипт Perl, подсчитывающий количество Fs и Ts в строке:

my $str = "GGGFFEEIIEETTGGG";
my $ft_count = 0;
$ft_count++ while($str =~ m/[FT]/g);
print "$ft_count\n";

Есть ли более краткий способ подсчета (другими словами, чтобы объединить строки 2 и 3)?

Ответы [ 4 ]

25 голосов
/ 28 сентября 2010
my $ft_count = $str =~ tr/FT//;

См. perlop .

Если REPLACEMENTLIST пуст, SEARCHLIST реплицируется.Это последнее полезно для подсчета символов в классе…

  $cnt = $sky =~ tr/*/*/;     # count the stars in $sky
  $cnt = tr/0-9//;            # count the digits in $_

Вот эталонный тест:

use strict; use warnings;

use Benchmark qw( cmpthese );

my ($x, $y) = ("GGGFFEEIIEETTGGG" x 1000) x 2;

cmpthese -5, {
    'tr' => sub {
        my $cnt = $x =~ tr/FT//;
    },
    'm' => sub {
        my $cnt = ()= $y =~ m/[FT]/g;
    },
};
Rate     tr      m
     Rate     m    tr
m   108/s    --  -99%
tr 8118/s 7440%    --

С ActiveState Perl 5.10.1.1006 на 32 Windows XP.

Разница, кажется, более значительна с

C:\Temp> c:\opt\strawberry-5.12.1\perl\bin\perl.exe t.pl
      Rate      m     tr
m   88.8/s     --  -100%
tr 25507/s 28631%     --
8 голосов
/ 28 сентября 2010

Когда оператор "m" имеет флаг / g и выполняется в контексте списка, он возвращает список совпадающих подстрок. Так что еще один способ сделать это будет:

my @ft_matches = $str =~ m/[FT]/g;
my $ft_count = @ft_matches; # count elements of array

Но это все еще две строки. Еще один странный трюк, который может сделать его короче:

my $ft_count = () = $str =~ m/[FT]/g;

"() =" заставляет "m" находиться в контексте списка. Присвоение списка с N элементами списку нулевых переменных на самом деле ничего не делает. Но затем, когда это выражение присваивания используется в скалярном контексте ($ ft_count = ...), правый оператор «=» возвращает количество элементов из его правой стороны - именно то, что вы хотите.

Это невероятно странно, когда впервые встречается, но идиома "= () =" - полезный трюк на Perl, который нужно знать для «оценки в контексте списка, а затем получения размера списка».

Примечание. У меня нет данных о том, какие из них более эффективны при работе с большими строками. На самом деле, я подозреваю, что ваш оригинальный код может быть лучшим в этом случае.

8 голосов
/ 28 сентября 2010

Да, вы можете использовать секретный оператор CountOf :

my $ft_count = ()= $str =~ m/[FT]/g;
0 голосов
/ 28 сентября 2010

Вы можете объединить строки 2, 3 и 4 в одну, например:

my $str = "GGGFFEEIIEETTGGG";
print $str =~ s/[FT]//g; #Output 4;
...