我有一个数组的引用(称为$intervals
),我想对这个数组中的值进行排序。数组中可能存在大量值,因此我不希望复制值。我目前的做法就是这样。
sub by_position
{
$a->start <=> $b->start ||
$a->end <=> $b->end
}
my @sorted_intervals = sort by_position (@$intervals);
但是,如果我正确理解Perl,这确实会复制数组中的所有值。是对的吗?如果是这样,有没有办法可以对数组进行就地排序(使用对该数组的引用)?
答案 0 :(得分:25)
Perl允许使用习语@arr = sort @arr
就地对数组进行排序。与赋值运算符的正常行为相反,在这种情况下不会复制。但是,这种优化仅限于正常的数组变量;它不适用于数组引用:
让我们使用-MO=Concise
选项查看内幕。首先,我们进行正常的就地排序,看看我们期望的是什么:
$ perl -E'say $^V'
v5.18.2
$ perl -MO=Concise -e'my @arr; @arr = sort @arr'
8 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 1 -e:1) v:{ ->3
3 <0> padav[@arr:1,2] vM/LVINTRO ->4
4 <;> nextstate(main 2 -e:1) v:{ ->5
- <1> ex-aassign vKS/64 ->8
- <1> ex-list lK ->-
5 <0> pushmark s ->6
7 <@> sort lK/INPLACE ->8
6 <0> padrange[@arr:1,2] l/1 ->7
- <0> padav[@arr:1,2] lRM* ->7
- <1> ex-list lK ->-
- <0> ex-pushmark s ->-
- <0> ex-padav lRM* ->-
-e syntax OK
有趣的是:<@> sort lK/INPLACE ->8
,似乎已经到位了。现在让我们用引用做同样的事情:
$ perl -MO=Concise -e'my $ref; @$ref = sort @$ref'
e <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 1 -e:1) v:{ ->3
3 <0> padsv[$ref:1,2] vM/LVINTRO ->4
4 <;> nextstate(main 2 -e:1) v:{ ->5
d <2> aassign[t4] vKS/COMMON ->e
- <1> ex-list lK ->a
5 <0> pushmark s ->6
9 <@> sort lK ->a
6 <0> pushmark s ->7
8 <1> rv2av[t3] lK/1 ->9
7 <0> padsv[$ref:1,2] s ->8
- <1> ex-list lK ->d
a <0> pushmark s ->b
c <1> rv2av[t2] lKRM*/1 ->d
b <0> padsv[$ref:1,2] sM/DREFAV ->c
-e syntax OK
我在<@> sort lK ->a
中没有看到一个就位标志。因此,优化仅在使用相同变量时才起作用,而不是在使用相同数组时。但这意味着如果我们将数组变量别名为某个标量引用的数组(使用Data::Alias),我们就可以对数组引用进行排序:
perl -MData::Alias -MO=Concise -e'my $ref; alias my @arr = @$ref; @arr = sort @arr'
e <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 1 -e:1) v:{ ->3
3 <0> padsv[$ref:1,3] vM/LVINTRO ->4
4 <;> nextstate(main 2 -e:1) v:{ ->5
- <1> entersub vKS/INARGS ->a
...
a <;> nextstate(main 3 -e:1) v:{ ->b
- <1> ex-aassign vKS/64 ->e
- <1> ex-list lK ->-
b <0> pushmark s ->c
d <@> sort lK/INPLACE ->e
c <0> padrange[@arr:2,3] l/1 ->d
- <0> padav[@arr:2,3] lRM* ->d
- <1> ex-list lK ->-
- <0> ex-pushmark s ->-
- <0> ex-padav lRM* ->-
-e syntax OK
... inplace-flag再次出现<@> sort lK/INPLACE ->e
: - )
这意味着Eric Strom's answer不正确。
答案 1 :(得分:17)
从perl 5.8.4开始,优化了就地排序@a = sort @a
。有关详细信息,请参阅以下链接:
Performance Enhancements in perl584delta
http://perl5.git.perl.org/perl.git/commit/fe1bc4cf71e7b04d33e679798964a090d9fa7b46?f=pp_sort.c
+ /* optimiser converts "@a = sort @a" to "sort \@a";
+ * in case of tied @a, pessimise: push (@a) onto stack, then assign
+ * result back to @a at the end of this function */
所以你应该写:
@$intervals = sort by_position @$intervals
在perl的5.8.3之后你会发现内存使用量减少(并且在极少数情况下保留别名)。
答案 2 :(得分:2)
第二个示例将创建一个新引用:$x = [ qw( a c b ) ]; say $x; @$x = sort @$x; say $x; $x = [sort @$x]; say $x
。 第一个例子按你的意愿行事。 ref。