修改排序比较器内的字符串

时间:2018-05-03 07:33:27

标签: regex perl

我有以下代码,在排序比较器中,它会在进行比较之前删除前缀字符串。

print for sort { 
   $a =~ s/^STRING//;  
   $b =~ s/^STRING//;  
   foo($a) cmp foo($b)     
} @a;  

虽然比较和顺序正确,但是正在从输出中删除前缀字符串 以下保留前缀字符串(如我所愿)。

print for sort { 
   $x = a;  
   $y = b;  
   $x =~ s/^STRING//;  
   $y =~ s/^STRING//;  
   foo($x) cmp foo($y)     
} @a;    

但我很困惑第二部分如何保留前缀 它是否正在执行字符串的副本,并且在数组的情况下剥离原始引用?
我是否在第一段片段中做错了什么并最终解决了问题?

2 个答案:

答案 0 :(得分:7)

一种方法:使用/r modifier

print for sort {
    foo( $a =~ s/^STRING//r ) cmp foo( $b =~ s/^STRING//r )
} @ary

替换运算符s/返回修改后的字符串并保持原始状态不变。如果没有匹配,则返回原始字符串,这似乎符合目的。如果在大型数组上使用,或者函数调用需要时间,请参见最后的优化。

另一种方法是将替换更改为匹配和捕获,何时 可行的。

sort对此进行了广泛的讨论。首先,$a$b是(包)全局

  

$a$b设置为包中的包全局变量从

调用sort()

{ }代表匿名子程序和

  

...要比较的元素作为包全局变量$a$b

传递到子例程

因此别名意味着更改它们会影响元素。因此

  

要比较的值始终按引用传递,不应修改。

$a$b发生变化以便元素发生变化时会发生什么。

在第二种情况下,您将$a$b复制到(应该是什么!)词汇$x$y,并且与@ary的连接被破坏因此元素不会改变。

始终在程序开头有use warnings;use strict;。这是一个很好的,如果是极端的例子 - 你介绍的用于尝试的变量是全局的($x)还是词汇的(my $x)可能很重要。

处理元素以使用结果值进行排序比较的代码存在效率缺陷。比较一次在两个元素上完成,因此元素被多次处理。每次我们进行相同的处理并为元素计算相同的值。

这种低效率仅适用于足够大的数据集,并且大部分时间都不用担心。但在这种情况下,正则表达式引擎运行并且还涉及函数调用,并且这些在Perl中并不完全便宜。此外,调用的开销未指定,我想有一些工作涉及。

要优化此功能,可以预处理输入然后排序

my @sorted_ary =  
    map { $_->[1] }
    sort { $a->[0] cmp $b->[0] }
    map { [ foo( s/^STRING//r ), $_ ] }
    @ary;

获取输入map的{​​{1}}应用正则表达式和函数调用,并将结果与​​原始元素一起存储在双元素arrayref中,用于{{1}的每个元素}}。然后使用第一个arrayref元素进行@ary这个arrayrefs列表进行比较。最后一个@ary从排序的arrayrefs中提取第二个元素,从而返回根据需要排序的原始项目。

这称为Schwartzian transform。例如,请参阅sort中的“示例”。

应该记住,只有足够大的数据才能获得增益,而这种操作也带来了开销(以及更复杂的代码)。因此,只有在存在可分类的问题时才考虑使用它。

答案 1 :(得分:1)

请考虑此代码,详细说明我的previous comment

use strict;
use warnings;

use v5.14;

my @array=qw(a b c d foo bar baz); #This creates the array
my @prefixed = map { "STRING$_"} @array;

#This is the sorting part we're interested in
say for sort { sans_prefix($a) cmp sans_prefix($b) } @prefixed;

# This sub does the extraction
sub sans_prefix {
  my ($no_prefix) = ($_[0] =~ /STRING(\w+)/);
  return $no_prefix;
}

提取非前缀的部分的逻辑隐藏在sans_prefix中。我们没有做任何替换,但无论如何我们都是在一个词汇($ _ [0])上行动而不是修改它,所以这会返回

STRINGa
STRINGb
STRINGbar
STRINGbaz
STRINGc
STRINGd
STRINGfoo