在Perl中交换两个数组值的最有效方法

时间:2012-03-21 18:07:06

标签: perl

我很好奇在Perl中交换数组中两个元素的最有效方法是什么。

假设我想分别用3和5的索引切换元素,那么最快的方法是什么?

我的想法是这样的:

@array[3,5] = @array[5,3];

这非常有效吗?我希望如果这两个数组值包含大量数据,那就不会有什么不同。我希望后端能够改变指针,而不进行任何实际的复制。这是对的吗?

4 个答案:

答案 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%    --