我很好奇在Perl中交换数组中两个元素的最有效方法是什么。
假设我想分别用3和5的索引切换元素,那么最快的方法是什么?
我的想法是这样的:
@array[3,5] = @array[5,3];
这非常有效吗?我希望如果这两个数组值包含大量数据,那就不会有什么不同。我希望后端能够改变指针,而不进行任何实际的复制。这是对的吗?
答案 0 :(得分:7)
你是什么意思“大量数据”?如果您指的是对大型数据结构的引用,那么您的工作方式并不重要;它会很快疯狂。 (每次交换不到一纳秒。)所以我假设你的意思是“大字符串”。
在5.20之前,您发布的代码将复制字符串。您可以使用Data::Alias来避免这种情况。
use Data::Alias;
alias @array[3,5] = @array[5,3];
如果你想避免Data :: Alias的语法魔法,你可以用一点XS做同样的事情:
void swap_array_elements(AV * av, IV ix1, IV ix2) {
SV ** p_ele1 = av_fetch(av, ix1, 1);
SV ** p_ele2 = av_fetch(av, ix2, 1);
SV * sv = *p_ele1;
*p_ele1 = *p_ele2;
*p_ele2 = sv;
}
从5.20开始,字符串副本被替换为简单的指针交换。
$ 5.18t/bin/perl -MDevel::Peek -e'
my @a = qw( a b c d e f );
Dump($a[3]);
Dump($a[5]);
@a[3,5] = @a[5,3];
Dump($a[3]);
Dump($a[5]);
' 2>&1 | grep '^ PV'
PV = 0x353fba0 "d"\0
PV = 0x357abb0 "f"\0
PV = 0x3541ff0 "f"\0
PV = 0x3537940 "d"\0
$ 5.20t/bin/perl -MDevel::Peek -e'
my @a = qw( a b c d e f );
Dump($a[3]);
Dump($a[5]);
@a[3,5] = @a[5,3];
Dump($a[3]);
Dump($a[5]);
' 2>&1 | grep '^ PV'
PV = 0xe9ace0 "d"\0
PV = 0xe8f230 "f"\0
PV = 0xe8f230 "f"\0
PV = 0xe9ace0 "d"\0
这意味着直截了当的@array[3,5] = @array[5,3];
会很好。
答案 1 :(得分:4)
运行bvr的基准脚本并进行修改:
my @array = ('thing' x 1E5, 'stuff' x 5E5, 'that' x 1E6, 'joe' x 5E6,
'sofa' x 1E7, 'jim' x 5E7);
结果:
Rate slice temp alias
slice 1.98/s -- -69% -100%
temp 6.47/s 227% -- -100%
alias 4218715/s 213045016% 65227727% --
答案 2 :(得分:3)
我的猜测是,通过temp变量进行的简单交换比切片更快。基准
use Benchmark qw(cmpthese);
use Data::Alias;
my @array = ('thing', 'stuff', 'that', 'joe', 'sofa', 'jim');
cmpthese(-2, {
slice => sub {
@array[3,5] = @array[5,3];
},
temp => sub {
my $tmp = $array[3];
$array[3] = $array[5];
$array[5] = $tmp;
},
alias => sub {
alias @array[3,5] = @array[5,3];
}
});
似乎证实:
Rate slice alias temp
slice 940155/s -- -70% -73%
alias 3151934/s 235% -- -9%
temp 3472932/s 269% 10% --
修改:在我看到ikegami的评论后,更改为字符串并添加了alias
。看起来像temp变量仍然是最快的。
答案 3 :(得分:0)
对于交换第一个和最后一个值的特殊情况,您可以使用:
push @l, shift @l;
基准测试结果:
cmpthese( 10000000, {
tmp => sub{
my @l = ( 1, 2 );
my $tmp = $l[0];
$l[0] = $l[1];
$l[1] = $tmp;
},
shift => sub{ #
my @l = ( 1, 2 );
push @l, shift @l;
},
slice => sub{
my @l = ( 1, 2 );
@l[0,1] = @l[1,0];
},
});
Rate slice tmp shift
slice 2544529/s -- -4% -38%
tmp 2645503/s 4% -- -36%
shift 4115226/s 62% 56% --