如何将Perl Map函数转换为Python? [关闭]

问题描述 投票:0回答:2

我想将一段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分配一个数组,但我只是对地图函数和移位运算符以及它如何应用于@k感到困惑

我该如何解决这个问题?

python regex perl
2个回答
5
投票

虽然你得到了ysth的完整解释,但我无法帮助它,只能改写它

一个简单的例子

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+:(连续数字后跟:)修剪,然后分成单词。所有仅为数字的单词总结。这用于查找最大(“最佳”)此类总和并记录其元素。

一些评论

  • foreach在处理中对数组元素进行别名,因此如果操作更改当前处理的元素,则数组也会更改。 原始代码不会改变@M的元素,所以我首先将$m复制到$new_m并使用它。如果这无关紧要,@M可能会在这个循环中改变,只需做$m =~ s/\d+://g;,并在其他地方使用$m而不是$new_m
  • 不使用index $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的评论。


4
投票

map通常用于将一个列表转换为另一个列表;在这里它被滥用简单地循环@m

代码相当于:

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

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

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

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