【问题标题】:How can I count characters in Perl?如何计算 Perl 中的字符数?
【发布时间】:2010-09-28 14:25:54
【问题描述】:

我有以下 Perl 脚本计算字符串中 Fs 和 Ts 的数量:

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

有没有更简洁的方法来获取计数(换句话说,合并第 2 行和第 3 行)?

【问题讨论】:

    标签: regex perl charactercount


    【解决方案1】:
    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;
        },
    };
    
     速率 tr m
         速率 m tr
    米 108/s -- -99%
    tr 8118/s 7440% --

    在 32 Windows XP 上使用 ActiveState Perl 5.10.1.1006。

    区别似乎更明显

    C:\Temp> c:\opt\strawberry-5.12.1\perl\bin\perl.exe t.pl
          速率 m tr
    米 88.8/s -- -100%
    tr 25507/s 28631% --

    【讨论】:

    • 这应该是my $ft_count = $str =~ tr/FT/FT/;,否则会删除$str中的字符
    • 当然 tr/// 不是正则表达式,所以 从技术上讲它不能回答具体问题 :-) 但这比使用正则表达式要好得多。跨度>
    • 你的基准是让“m”的情况下只找到第一个匹配,因为正则表达式匹配是在标量上下文中。如果我将该行修正为“my $cnt = () = $y =~ m/[FT]/g;”,“tr”最终比“m”(在我的 Linux 机器上)好 3000%。顺便说一句,原始代码的速度大约是“m”的两倍。
    • @Sinan +1 推荐tr///。我认为你的基准有一个错误。为了计算正则表达式的替换,您需要一个干预列表上下文:my $cnt = ()= $y =~ m/[FT]/g;。当你以这种方式运行时,tr/// 比m// 快得多。我也在 ActivePerl 下的 v5.10 上。
    • @Sinan Ünür 这就是为什么在基准测试之前有一个测试部分是个好主意。我通常将要测试的 lambda 填充到哈希中,遍历哈希打印其返回值,然后执行基准测试。如果任何值不同,那么我知道我的基准测试不佳。
    【解决方案2】:

    是的,您可以使用CountOf secret operator:

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

    【讨论】:

    • 也称为山羊算子 ;) =()=
    • 天哪,为什么...... tyvm Daenyth......
    【解决方案3】:

    当“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 技巧,用于“在列表上下文中评估,然后获取列表的大小”。

    注意:在处理大字符串时,我没有关于哪种方法更有效的数据。事实上,我怀疑你的原始代码在这种情况下可能是最好的。

    【讨论】:

      【解决方案4】:

      您可以像这样将第 2、3 和 4 行合并为一个:

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

      【讨论】:

      • 作为对另一个答案的评论,这比作为评论更好:)
      • @ysh,感谢您的评论。我没有意识到我的答案实际上是对另一个答案的评论,是吗? OP 询问 [是否有更简洁的方法来获取计数(换句话说,将第 2 行和第 3 行合并),这是我对这个问题的回答。有人已经提过我的建议了吗?
      • @ysth,原始帖子可能与此 [stackoverflow.com/questions/1849329/… 重复,我针对该问题发布了类似的解决方案。我认为这篇文章可以与那篇文章结合起来。
      猜你喜欢
      • 1970-01-01
      • 2023-03-16
      • 1970-01-01
      • 2021-05-28
      • 1970-01-01
      • 1970-01-01
      • 2021-10-25
      • 1970-01-01
      • 2020-03-12
      相关资源
      最近更新 更多