هل يوجد حل Perl للقوائم البطيئة في هذا الجانب من Perl 6؟

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

  •  02-07-2019
  •  | 
  •  

سؤال

هل وجد أي شخص حلاً جيدًا للقوائم التي تم تقييمها بتكاسل في بيرل؟لقد جربت عددًا من الطرق لتحويل شيء مثل

for my $item ( map { ... } @list ) { 
}

في تقييم كسول - عن طريق ربط @list، على سبيل المثال.أحاول تجنب التقسيم وكتابة مرشح مصدر للقيام بذلك، لأنه يعبث بقدرتك على تصحيح أخطاء التعليمات البرمجية.هل حقق أي شخص أي نجاح.أو هل عليك فقط التقسيم واستخدام حلقة while؟

ملحوظة: أعتقد أنه يجب أن أذكر أنني مدمن مخدرات على سلاسل grep-map الطويلة أحيانًا لتحويل القوائم وظيفيًا.لذلك فهي ليست حلقة foreach أو حلقة while.إنها تعبيرات الخريطة التي تميل إلى تجميع المزيد من الوظائف في نفس المساحة الرأسية.

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

المحلول

كما ذكرنا سابقًا، for(each) عبارة عن حلقة حريصة، لذا فهي تريد تقييم القائمة بأكملها قبل البدء.

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

بدون كتابة فصل دراسي كامل أو استخدام أي وحدات، يمكنك إنشاء مصنع مكرر بسيط فقط باستخدام عمليات الإغلاق:

sub make_iterator {
    my ($value, $max, $step) = @_;

    return sub {
        return if $value > $max;    # Return undef when we overflow max.

        my $current = $value;
        $value += $step;            # Increment value for next call.
        return $current;            # Return current iterator value.
    };
}

ومن ثم استخدامه:

# All the even numbers between 0 -  100.
my $evens = make_iterator(0, 100, 2);

while (defined( my $x = $evens->() ) ) {
    print "$x\n";
}

هناك أيضا التعادل::صفيف::كسول الوحدة النمطية الموجودة على CPAN، والتي توفر واجهة أكثر ثراءً واكتمالًا للمصفوفات البطيئة.لم أستخدم الوحدة بنفسي، لذا قد تختلف المسافة المقطوعة.

أتمنى لك كل خير،

بول

نصائح أخرى

[ملاحظة جانبية:انتبه إلى أن كل خطوة فردية على طول سلسلة الخريطة/grep تكون متلهفة.إذا أعطيتها قائمة كبيرة مرة واحدة، فإن مشاكلك تبدأ في وقت أقرب بكثير مما كانت عليه في المباراة النهائية foreach.]

ما يمكنك فعله لتجنب إعادة الكتابة بالكامل هو لف الحلقة بحلقة خارجية.بدلاً من كتابة هذا:

for my $item ( map { ... } grep { ... } map { ... } @list ) { ... }

…اكتبها هكذا:

while ( my $input = calculcate_next_element() ) {
    for my $item ( map { ... } grep { ... } map { ... } $input ) { ... }
}

وهذا يوفر عليك من الاضطرار إلى إعادة كتابة التعليمات البرمجية الموجودة لديك بشكل كبير، وطالما أن القائمة لا تنمو بعدة أوامر من حيث الحجم أثناء التحويل، فستحصل تقريبًا على كل الفوائد التي ستوفرها إعادة الكتابة إلى نمط التكرار.

إذا كنت تريد إنشاء قوائم كسولة، فسيتعين عليك كتابة المكرر الخاص بك.بمجرد حصولك على ذلك، يمكنك استخدام شيء من هذا القبيل الكائن::تكرار الذي يحتوي على إصدارات مدركة للتكرار من map و grep.ألقِ نظرة على مصدر تلك الوحدة:إنه أمر بسيط جدًا وسترى كيفية كتابة الإجراءات الفرعية الخاصة بك والمدركة للمكرر.

حظ سعيد، :)

هناك حالة خاصة واحدة على الأقل حيث تم تحسين for وforeach بحيث لا يتم إنشاء القائمة بأكملها مرة واحدة.وهذا هو عامل النطاق.لذلك لديك خيار القول:

for my $i (0..$#list) {
  my $item = some_function($list[$i]);
  ...
}

وسيتم تكرار ذلك عبر المصفوفة، وتحويلها كيفما تشاء، دون إنشاء قائمة طويلة من القيم مقدمًا.

إذا كنت ترغب في أن يقوم بيان الخريطة الخاص بك بإرجاع أعداد متغيرة من العناصر، فيمكنك القيام بذلك بدلاً من ذلك:

for my $i (0..$#array) {
  for my $item (some_function($array[$i])) {
    ...
  }
}

إذا كنت ترغب في المزيد من الكسل المنتشر، فإن أفضل خيار لديك هو معرفة كيفية استخدام عمليات الإغلاق لإنشاء قوائم كسولة.كتاب MJD الممتاز بيرل من أجل العالي يمكن أن يرشدك من خلال هذه التقنيات.ومع ذلك، احذر من أنها ستتضمن تغييرات أكبر بكثير في التعليمات البرمجية الخاصة بك.

أعيد هذا من بين الأموات لأذكر أنني كتبت الوحدة للتو List::Gen على CPAN الذي يفعل بالضبط ما كان يبحث عنه الملصق:

use List::Gen;

for my $item ( @{gen { ... } \@list} ) {...}

جميع عمليات حساب القوائم كسولة، وهناك مكافئات للخريطة / grep بالإضافة إلى بعض الوظائف الأخرى.

تُرجع كل وظيفة "مولدًا" وهو إشارة إلى مصفوفة مرتبطة.يمكنك استخدام المصفوفة المرتبطة مباشرة، أو هناك مجموعة من طرق الوصول مثل المكررات التي يمكنك استخدامها.

استخدم المكرر أو النظر في استخدام التعادل::LazyList من CPAN (وهو مؤرخ قليلاً).

لقد طرحت سؤالا مماثلا في perlmonks.org, ، وقدم BrowserUk إطارًا جيدًا حقًا في جوابه.في الأساس، الطريقة الملائمة للحصول على تقييم كسول هي إنتاج سلاسل عمليات للحساب، على الأقل طالما أنك متأكد من أنك تريد النتائج، ليس الآن.إذا كنت تريد أن لا يؤدي التقييم البطيء إلى تقليل زمن الوصول ولكن لتجنب العمليات الحسابية، فلن يساعد أسلوبي لأنه يعتمد على نموذج الدفع، وليس نموذج السحب.ربما باستخدام كورومخططات تفصيلية، يمكنك تحويل هذا النهج إلى نموذج سحب (أحادي الترابط) أيضًا.

أثناء التفكير في هذه المشكلة، قمت أيضًا بالتحقق من ربط مصفوفة بنتائج سلسلة المحادثات لجعل برنامج Perl يتدفق بشكل أشبه map, ، ولكن حتى الآن، أحب واجهة برمجة التطبيقات (API) الخاصة بي لتقديم parallel "الكلمة الأساسية" (منشئ كائن مقنع) ثم استدعاء الأساليب على النتيجة.سيتم نشر النسخة الأكثر توثيقًا من الكود كرد على هذا الخيط وربما يتم إصداره على CPAN أيضًا.

إذا كنت أتذكر بشكل صحيح، for/foreach تحصل على القائمة بأكملها أولاً على أي حال، لذلك ستتم قراءة القائمة التي تم تقييمها بتكاسل بالكامل ثم ستبدأ في التكرار عبر العناصر.لذلك، أعتقد أنه لا توجد طريقة أخرى سوى استخدام حلقة while.ولكن قد أكون مخطئا.

تتمثل ميزة حلقة while في أنه يمكنك تزييف الإحساس بقائمة تم تقييمها بتكاسل باستخدام مرجع رمزي:

my $list = sub { return calculate_next_element };
while(defined(my $element = &$list)) {
    ...
}

بعد كل شيء، أعتقد أن التعادل هو أقرب ما يمكنك الحصول عليه في بيرل 5.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top