我最近在Perl 5中"需要"一个zip函数(当时我正在考虑如何计算相对时间?),即一个函数,它将两个列表和"拉链"一起放到一个列表中,交错元素.
(伪)例如:
@a=(1, 2, 3); @b=('apple', 'orange', 'grape'); zip @a, @b; # (1, 'apple', 2, 'orange', 3, 'grape');
哈斯克尔在前奏拉链和Perl 6的有一个拉链运营商内置的,但你怎么做它在Perl 5优雅的方式?
假设你有两个列表并且它们的长度完全相同,这里有一个最初由merlyn(Randal Schwartz)提出的解决方案,它称之为per per per per per:
sub zip2 { my $p = @_ / 2; return @_[ map { $_, $_ + $p } 0 .. $p - 1 ]; }
这里发生的是,对于10个元素的列表,首先,我们在中间找到枢轴点,在这种情况下为5,并将其保存$p
.然后我们制作一个到目前为止的索引列表,在这种情况下为0 1 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)] }
你是否想要使用这两种变化取决于你是否能够看到自己记住它们是如何工作的,但对我来说,它是一个思维扩展器.
该列表:: MoreUtils模块有一个拉链/网格功能应该做的伎俩:
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; }
我发现以下解决方案简单易读:
@a = (1, 2, 3); @b = ('apple', 'orange', 'grape'); @zipped = map {($a[$_], $b[$_])} (0 .. $#a);
我相信它也比首先以错误的顺序创建数组然后使用切片重新排序的解决方案或修改@a
和解决方案的解决方案更快@b
.
对于相同长度的数组:
my @zipped = ( @a, @b )[ map { $_, $_ + @a } ( 0 .. $#a ) ];