【问题标题】:Perl function for slicing string with start, end and stepPerl 函数用 start、end 和 step 对字符串进行切片
【发布时间】:2020-07-05 16:41:23
【问题描述】:

我想使用 start、end 和 step 对字符串进行切片。假设起始坐标为 1,结束坐标为 22,步长为 3。切片字符串应仅选择坐标 1、4、7、10、13、16、19、22 中的元素。我写了一个函数 get_subseq这样做。在 Perl 中有更短的方法吗?

sub get_subseq {
    my ( $seq, $start, $end, $step ) = @_;
    my $index = $start;
    while ( $index <= $end ) {
        print substr $seq, $index, 1;
        $index += $step;
    }
}

my $sequence = 'AGGGTAGAGTGAGAAGCACCAGCAGGCAGTAACAGC';

# The result should be GTAGACCC
get_subseq( $sequence, 1, 22, 3 );

【问题讨论】:

    标签: string perl


    【解决方案1】:

    一种方法:生成索引列表,然后使用map获取对应的字符。

    合并成一个语句

    use warnings;
    use strict;
    use feature 'say';
    
    my $seq = q(AGGGTAGAGTGAGAAGCACCAGCAGGCAGTAACAGC); 
    
    my ($beg, $end, $step) = (1, 22, 3); 
    
    my @subseq = 
        map { substr $seq, $_, 1 } 
        grep { ($_-$beg) % $step == 0 } 
        $beg..$end; 
    
    say "@subseq";
    

    这可以在$beg..$end 范围内折叠成一次迭代

    my @subseq = 
        map { ($_-$beg) % $step == 0 ? substr($seq, $_, 1) : () }
        $beg..$end; 
    

    如果结果需要是字符串join,则由'' 列出(空字符串)。

    当然还有一些库可以大步生成一个范围。 List::Gen 有这样的range 函数,同时它还有一系列有趣的算法。

    use List::Gen qw(range);
    
    my @ss = map { substr $seq, $_, 1 } @{ range $beg, $end, $step };
    
    say "@ss";
    

    它的range 返回一个真正的生成器,它带有有趣的属性。取消引用它会产生值列表。请参阅文档。

    虽然这些在单个语句中返回结果,因此“更短”,但我喜欢你自己的子问题,它非常清晰,在许多情况下可能更有效。

    通过(很少)使用 C 风格的 for 循环可以进一步简化它

    for (my $i = $beg; $i <= $end; $i += $step) { print substr $seq, $i, 1 }
    

    另一种方法是将字符串分解成一个字符列表,然后从该列表中提取所需位置的元素

    my @subseq = (split //, $seq)[ @indices ];
    

    您可以使用任何方法获取@indices(不必是数组,但可以是在此处生成的列表,例如通过上面使用的任何方法)。这两种方法中哪一种更有效完全取决于细节——序列的长度、要采样的索引跨度的长度、它们的关系、步长。

    【讨论】:

      【解决方案2】:

      没有人喜欢使用正则表达式?

      join'',substr($seq,$start,$end-$start+1)=~/(?=(.)).{0,$step}/gs
      

      这演示了对这种事情使用正则表达式匹配,这种方式有时比循环或拆分和切片更有效。一种更有趣但效率不高的方法是避免使用 substr:

      join '',$seq=~/(?<=.{$start})(?<!..{$end})(?=(.)).{0,$step}/gs
      

      【讨论】:

      • 我愿意,尤其是这个 :)
      • @zdim 为您添加了另一种娱乐方式
      【解决方案3】:

      可以通过在感兴趣的位置取字符来找到解决方案。

      步骤 #1:生成索引数组

      步骤 #2:将 $seq 拆分为数组

      步骤#3:选择$seq数组索引位置的字符

      步骤#4:连接结果字符

      步骤#5:输出结果

      比多次使用substr快很多

      use strict;
      use warnings;
      use feature 'say';
      
      my $seq = 'AGGGTAGAGTGAGAAGCACCAGCAGGCAGTAACAGC';
      my($pos,$end,$inc) = (1,22,3);
      my @index;
      
      for(;$pos<=$end;$pos+=$inc) { push @index, $pos; }
      
      say join('',(split('',$seq))[@index]);
      

      输出

      GTAGACCC
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-13
        • 2017-09-28
        • 2014-07-08
        • 1970-01-01
        • 2021-03-12
        • 2017-06-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多