如何将Perl Map函数转换为Python?

时间:2019-05-01 06:16:51

标签: python regex perl

我想将一段perl代码转换为python,但是我对perl及其语法一点都不熟悉。

特别是,我只是对perl中的map运算符和下面的代码中的shift运算符感到困惑。

(@M) = ($y =~ m/((?:\d+:ITEM7 \d+:\d+ )+(?:\d+:ITEM7A \d+:\d+ )*)(?:\d+:ITEM8 \d+:\d+\s*)+/g);

$best = 0;
$bestseq = "";
for($i = 0; $i < scalar(@M); ++$i) {
  $m = $M[$i];
  $m =~ s/\d+://g;
  (@m) = (split / /, $m);
  $v = 0;
  $z = length_in_words($M[$i]);
  map { $v += $_ if($_ =~ m/^\d+$/); } @m;
  if($v > $best) { $best = $v; $bestseq = $M[$i]; }
}

sub length_in_words {
  my $x = shift;
  my @k;
  return scalar(@k = $x =~ m/(\S+)/sg);
}

我知道@M基于python re.findall分配了一个数组,但我对map函数以及将其应用于@k的shift运算符感到困惑

如何解决此问题?

2 个答案:

答案 0 :(得分:5)

虽然您已经得到了完整的解释,但我还是不得不重写

一个简单的例子

use warnings;
use strict;

use List::Util qw(sum0);    

my $y = ...    
my @M = $y =~ /.../;

my ($best, $bestseq) = (0, '');

foreach my $m (@M) {
    (my $new_m = $m) =~ s/\d+://g;
    my @w = split ' ', $new_m;         # CHANGED from original
    my $v = sum0 grep { /^\d+$/ } @w; 
    if ($v > $best) { 
        $best = $v; 
        $bestseq = $new_m; 
    }
}

sub length_in_words {
    return scalar split ' ', $_[0];
}

循环似乎执行以下操作。 @M数组的每个元素都将被\d+:(连续数字后跟:)修剪,然后拆分为单词。所有仅是数字的单词相加。这用于查找最大(“最佳”)此类和并记录其元素。

一些评论

  • A foreach 别名处理中的数组元素,因此,如果操作更改了当前处理的元素,则数组也会更改。

    原始代码不会更改@M的元素,因此我首先将$m复制到$new_m中并进行处理。如果没关系,并且@M可能在此循环中发生更改,只需执行$m =~ s/\d+://g;,然后在其他地方使用$m而不是$new_m

  • 索引$i未使用,因此直接遍历数组元素

  • $z未使用,已删除

  • map总结了@m的元素的一个过滤后的子集(仅数字)

  • 原始“最佳”初始化为零,因此使用sum0,它使用空列表输入给出零。

  • 原始split / /, $m单个空格分割标量$m。我强烈怀疑这样做的目的是将$m解析为单词,因此按所有连续的空格分隔。因此,我使用

    my @w = split ' ', $new_m;
    

    其中' '是一种特殊的模式,它可以分割任意数量的空格,并且也删除前导和尾随空格。参见split。我将其重命名为@w,因为它显然是单词。

另外一条评论:我将使用@words$val(或类似名称)而不是一个字母的名称。


length_in_words()中使用的代码和效率的评论。

当然,有多种方法可以计算字符串中的单词数。下面的基准测试显示了这里选择最快的基准测试

use warnings;
use strict;
use feature 'say';

use Benchmark 'cmpthese';

my $run_for = shift // 3;  # seconds for each

my $text = " ah\n no \t hi  end ";

sub split_scalar {
   return scalar split ' ', $_[0];
}  

sub regex_context {
    my $wc =()= $_[0] =~ /\S+/g;
}   

sub regex_while {
    my $wc; 
    ++$wc while $_[0] =~ /\S+/g;
    return $wc; 
}   

cmpthese (-$run_for, {
    split_scalar  => sub { split_scalar($text) },
    regex_context => sub { regex_context($text) },
    regex_while   => sub { regex_while($text) },
}); 

根据v5.24.4在可打印的桌面上

                    Rate regex_context regex_while  split_scalar  
regex_context  1119833/s            --         -7%          -90%
regex_while    1203020/s            7%          --          -89%
split_scalar  11351365/s          914%        844%            --

对于一个人来说,在标量环境中使用split如此巨大的优势令我感到惊讶,我猜想这是由于split中的特定优化所致,这种用例可从中受益。

更有趣的是,当使用10_000个单词分割字符串时,标量分割方式更为优越-达到 4026%

在我的测试中,这表明在v5.16.3和v5.24.4的台式机和服务器上重复运行具有一致的结果,在较旧的Perl下存在以下细微差别。

使用v5.16时,split的优势要小一些(尽管仍然是7倍),而在正则表达式中使用=()=进行上下文播放并没有比分配给数组并返回它的scalar(在v5.24中,它是30-40%,所以我省略了生成数组变量的情况)。

但是,请注意,在v5.12之前,标量上下文中的split具有surprising (nasty) behavior。给定问题中的代码,很可能会在较旧的Perl上运行(运行?)(这不能代替显示的代码),在这种情况下,请使用基于正则表达式的替代项split

感谢melpomene的评论。

答案 1 :(得分:4)

map通常用于将一个列表转换为另一个列表;此处只是简单地遍历@m就被滥用了。

代码等效于:

for my $maybe_number (@m) {
    if ($maybe_number =~ /^\d+$/) {
        $v += $maybe_number;
    }
}

总结@m的所有元素,只有一个或多个数字。

子内部的

my $x = shift;将子元素的第一个参数分配给$x

最后一行的单词长度找到所有非空白字符序列,并将它们分配给@k。该分配放置在标量上下文中,该标量上下文返回分配的元素数。因此,它计算的是子参数中有多少个“单词”(非空白字符的序列)。