我有以下代码,在排序比较器中,它会在进行比较之前删除前缀字符串。
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;
但我很困惑第二部分如何保留前缀
它是否正在执行字符串的副本,并且在数组的情况下剥离原始引用?
我是否在第一段片段中做错了什么并最终解决了问题?
答案 0 :(得分:7)
一种方法:使用/r
modifier
print for sort {
foo( $a =~ s/^STRING//r ) cmp foo( $b =~ s/^STRING//r )
} @ary
替换运算符s/
返回修改后的字符串并保持原始状态不变。如果没有匹配,则返回原始字符串,这似乎符合目的。如果在大型数组上使用,或者函数调用需要时间,请参见最后的优化。
另一种方法是将替换更改为匹配和捕获,何时 可行的。
sort对此进行了广泛的讨论。首先,$a
和$b
是(包)全局
将
调用sort()$a
和$b
设置为包中的包全局变量从
块{ }
代表匿名子程序和
...要比较的元素作为包全局变量
传递到子例程$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