Perl 5中是否有一个优雅的zip来交错两个列表?

Perl 5中是否有一个优雅的zip来交错两个列表?,perl,zip,Perl,Zip,我最近在Perl 5中“需要”了一个zip函数(当时我正在考虑),即一个函数,它将两个列表“打包”到一个列表中,并将元素交错 (伪)示例: 内置,但如何在Perl 5中以优雅的方式实现它?该模块有一个zip/mesh函数,可以实现以下功能: use List::MoreUtils qw(zip); my @numbers = (1, 2, 3); my @fruit = ('apple', 'orange', 'grape'); my @zipped = zip @numbers, @fru

我最近在Perl 5中“需要”了一个zip函数(当时我正在考虑),即一个函数,它将两个列表“打包”到一个列表中,并将元素交错

(伪)示例:

内置,但如何在Perl 5中以优雅的方式实现它?

该模块有一个zip/mesh函数,可以实现以下功能:

use List::MoreUtils qw(zip);

my @numbers = (1, 2, 3);
my @fruit = ('apple', 'orange', 'grape');

my @zipped = zip @numbers, @fruit;
以下是网格函数的来源:

sub mesh (\@\@;\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@\@) {
    my $max = -1;
    $max < $#$_  &&  ($max = $#$_)  for @_;

    map { my $ix = $_; map $_->[$ix], @_; } 0..$max; 
}
子网格(\@;\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\@){
我的$max=-1;
“@”的$max<$\$$&($max=$\$$);
映射{my$ix=$\uu;映射$\u->[$ix],@\u;}0..$max;
}
my@l1=qw/1 2 3/; my@l2=qw/7 8 9/; 我的孩子出去了; 在(@l1 | |@l2)时推出@out,移位@l1,移位@l2; 如果列表的长度不同,这将在额外的插槽中放置“undf”,但如果您不希望这样做,可以很容易地进行补救。类似(@l1[0]&&shift@l1)的东西可以做到这一点


希望这有帮助

假设您正好有两个列表,并且它们的长度完全相同,下面是merlyn(Randal Schwartz)最初提出的一个解决方案,他称之为反常的perlish:

sub zip2 {
    my $p = @_ / 2; 
    return @_[ map { $_, $_ + $p } 0 .. $p - 1 ];
}

这里发生的是,对于一个10元素列表,首先,我们发现中间的枢轴点,在这种情况下,5,并将其保存在<代码> $p/COD>。然后我们列一个到那个点的索引列表,在这个例子中是01 2 3 4。接下来,我们使用

map
将每个索引与另一个索引配对,该索引与轴心点的距离与第一个索引从一开始的距离相同,因此(在本例中)为0 5 1 6 2 7 3 8 4 9。然后我们从
@
中取一个切片,将其用作索引列表。这意味着如果将
'a',b',c',1,2,3
传递给
zip2
,它将返回重新排列为
'a',1',b',2',c',3
的列表

这可以用一个表达式沿着ysth的行写,如下所示:

sub zip2 { @_[map { $_, $_ + @_/2 } 0..(@_/2 - 1)] }
你是否想使用这两种变体取决于你是否能看到自己记住它们是如何工作的,但对我来说,这是一种思维拓展器。

如果你做了很多这类事情,那真的很好

我自己的代码:

sub zip { @_[map $_&1 ? $_>>1 : ($_>>1)+($#_>>1), 1..@_] }

这完全不是一个优雅的解决方案,也不是任何想象中的最佳解决方案。但这很有趣

package zip;

sub TIEARRAY {
    my ($class, @self) = @_;
    bless \@self, $class;
}

sub FETCH {
    my ($self, $index) = @_;
    $self->[$index % @$self][$index / @$self];
}

sub STORE {
    my ($self, $index, $value) = @_;
    $self->[$index % @$self][$index / @$self] = $value;
}

sub FETCHSIZE {
    my ($self) = @_;
    my $size = 0;
    @$_ > $size and $size = @$_ for @$self;
    $size * @$self;
}

sub CLEAR {
    my ($self) = @_;
    @$_ = () for @$self;
}

package main;

my @a = qw(a b c d e f g);
my @b = 1 .. 7;

tie my @c, zip => \@a, \@b;

print "@c\n";  # ==> a 1 b 2 c 3 d 4 e 5 f 6 g 7

如何处理
STORESIZE
/
推送
/
弹出
/
移位
/
取消移位
/
拼接
是留给读者的练习。

对于相同长度的阵列:

my @zipped = ( @a, @b )[ map { $_, $_ + @a } ( 0 .. $#a ) ];

我发现以下解决方案简单易懂:

@a = (1, 2, 3);
@b = ('apple', 'orange', 'grape');
@zipped = map {($a[$_], $b[$_])} (0 .. $#a);

我相信它也比先以错误的顺序创建阵列然后使用slice重新排序的解决方案,或者修改
@a
@b
的解决方案快得多,我可能已经表达了不修改两个输入列表的偏好:-)在C中使用位移位可能更快,但在Perl中只是不必要的混淆。最好这样写:@[map{${,$$+@{U2}0..(@/U2-1)]也要短一些。这不是问题所在,但我的zip设计也适用于奇数个元素。那些在符号处转义的元素是什么?原型,说它需要两到32个数组参数,子元素会隐式接收它们作为arrayrefs。我的眼睛流血了!难道没有像\{32}或类似的快捷方式吗?@Roberto,没有,但这只是一个原型黑客,你的眼睛都因为错误的原因流血了。这个原型黑客意味着L::MU的
zip
只适用于实际数组变量,而不是任意列表。例如,不能使用
my%hash=zip(@ary[0..$#ary])
反转数组;您必须首先创建一个包含索引的数组变量。Haskell的zip不是您要寻找的:它返回对应对的列表,而不是交错元素的列表;Haskell列表包含单个类型的元素。当我在这里提到Haskell时,我并没有想到。通常当一个人认为他们想要一个zip时,就是从两个列表中创建一个哈希。在这种情况下,最好使用散列切片<代码>@hash{@keys}=@values。如果这里的情况不是这样,那么很抱歉发出噪音。对于大小不等的数组,这会有问题。@briandfoy您的评论有帮助,但是您知道您忘记了做什么吗?指出哪种方法适用于大小不等的阵列。(我正在买一个需要这样做的)我没有忘记。这个问题取决于您想对其余元素做什么。您应该提出另一个问题并指定约束条件。对于大小不等的数组,这会有问题。
my @zipped = ( @a, @b )[ map { $_, $_ + @a } ( 0 .. $#a ) ];
@a = (1, 2, 3);
@b = ('apple', 'orange', 'grape');
@zipped = map {($a[$_], $b[$_])} (0 .. $#a);