我想将一段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运算符感到困惑
如何解决此问题?
答案 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
。该分配放置在标量上下文中,该标量上下文返回分配的元素数。因此,它计算的是子参数中有多少个“单词”(非空白字符的序列)。