هل هناك ملف مضغوط أنيق لتداخل قائمتين في Perl 5؟

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

  •  09-06-2019
  •  | 
  •  

سؤال

لقد "أحتاجت" مؤخرًا إلى وظيفة مضغوطة في Perl 5 (بينما كنت أفكر في كيف أحسب الوقت النسبي؟)، أي.دالة تأخذ قائمتين و"تضغطهما" معًا في قائمة واحدة، مع تشذير العناصر.

مثال (زائف):

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

هاسكل لديه الرمز البريدي في المقدمة و يحتوي Perl 6 على مشغل مضغوط مدمج، ولكن كيف يمكنك القيام بذلك بطريقة أنيقة في Perl 5؟

هل كانت مفيدة؟

المحلول

بافتراض أن لديك قائمتين بالضبط ولهما نفس الطول تمامًا، فإليك الحل الذي قدمه في الأصل ميرلين (راندال شوارتز)، الذي أطلق عليه اسم "الهالك المنحرف":

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;

هنا مصدر وظيفة الشبكة:

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 );

إذا كانت القوائم بأطوال مختلفة، فسيؤدي ذلك إلى وضع "غير محدد" في الفتحات الإضافية ولكن يمكنك معالجة ذلك بسهولة إذا كنت لا ترغب في القيام بذلك.شيء مثل ( @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