【问题标题】:How to Convert Perl Map function to Python? [closed]如何将 Perl 映射函数转换为 Python? [关闭]
【发布时间】:2019-09-19 18:56:28
【问题描述】:

我想将一段 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 函数以及 shift 运算符及其如何应用于 @k 感到困惑

我该如何解决这个问题?

【问题讨论】:

  • 噗,太吓人了:/
  • Stack Overflow 不是免费的代码编写服务,要求我们完全重写 Python 可能会使您的问题过于宽泛。
  • map 函数用于增加 $v 的副作用

标签: python regex perl


【解决方案1】:

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

代码相当于:

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

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

my $x = shift; 在 sub 中将第一个参数分配给 sub 到 $x

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

【讨论】:

  • 谢谢。所以只是为了澄清@m 是一个在$m 的空白处分割的数组(由(split / / @m) 给出,然后maybe_number 迭代@m 中的每个元素。这是正确的吗?
  • 是的,没错
【解决方案2】:

虽然你已经得到了完整的解释,但我忍不住重写了

一个简单的例子

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

一些cmets

  • 一个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 下,在不错的桌面上打印

率 regex_context regex_while split_scalar 正则表达式_上下文 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 倍),并且在正则表达式中使用 =()= 的上下文播放并不比分配给数组并返回 @ 更好987654355@ (在 v5.24 中是 30-40%,所以我省略了生成数组变量的情况)。

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

感谢melpomene cmets。

【讨论】:

  • 当心:split 在标量上下文中对旧的 perls 有一个讨厌的副作用。
  • 应该是sum0,而不是sum,才能完美复制原版。
  • @melpomene 谢谢,修复并添加评论。还添加了一个基准,split 的表现令人惊讶。
猜你喜欢
  • 1970-01-01
  • 2019-07-12
  • 2020-10-17
  • 1970-01-01
  • 2013-11-02
  • 2020-01-05
  • 1970-01-01
  • 2020-08-17
  • 1970-01-01
相关资源
最近更新 更多