Perl 5에 두 개의 목록을 인터리브하는 우아한 zip이 있습니까?

StackOverflow https://stackoverflow.com/questions/38345

  •  09-06-2019
  •  | 
  •  

문제

나는 최근에 Perl 5에서 zip 기능이 "필요"했습니다. 상대 시간은 어떻게 계산하나요?), 즉.두 개의 목록을 가져와 하나의 목록으로 "압축"하여 요소를 인터리브하는 함수입니다.

(의사)예:

@a=(1, 2, 3);
@b=('apple', 'orange', 'grape');
zip @a, @b; # (1, 'apple', 2, 'orange', 3, 'grape');

Haskell에는 Prelude에 zip이 있습니다. 그리고 Perl 6에는 zip 연산자가 있습니다. 내장되어 있지만 Perl 5에서는 어떻게 우아한 방식으로 수행합니까?

도움이 되었습니까?

해결책

정확히 두 개의 목록이 있고 길이가 정확히 같다고 가정하면, 원래 merlyn(Randal Schwartz)이 작성한 솔루션이 있습니다. 그는 이를 Perverse Perlish라고 불렀습니다.

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)] }

두 변형 중 하나를 사용하고 싶은지 여부는 작동 방식을 기억할 수 있는지 여부에 따라 다르지만 저에게는 마음 확장기였습니다.

다른 팁

그만큼 목록::더보기Utils 모듈에는 트릭을 수행하는 zip/mesh 기능이 있습니다.

use List::MoreUtils qw(zip);

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

my @zipped = zip @numbers, @fruit;

mesh 함수의 소스는 다음과 같습니다.

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 ) ];
my @l1 = qw/1 2 3/;
my @l2 = qw/7 8 9/;
my @out; 
push @out, shift @l1, shift @l2 while ( @l1 || @l2 );

목록의 길이가 다른 경우 추가 슬롯에 'undef'가 추가되지만 이를 원하지 않는 경우 쉽게 해결할 수 있습니다.( @l1[0] && Shift @l1 )과 같은 것이 그렇게 할 것입니다.

도움이 되었기를 바랍니다!

Algorithm::Loops 이런 일을 많이 하면 정말 좋아요.

내 코드:

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/PUSH/POP/SHIFT/UNSHIFT/SPLICE 독자에게 남겨진 연습이다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top