Perl dizisi aracılığıyla yinelemenin en iyi yolu


96

Bir Perl dizisi aracılığıyla yineleme yapmak için en iyi uygulama (hız ve bellek kullanımı açısından) hangisidir? Daha iyi bir yol var mı? ( @Arraysaklanmasına gerek yoktur).

Uygulama 1

foreach (@Array)
{
      SubRoutine($_);
}

Uygulama 2

while($Element=shift(@Array))
{
      SubRoutine($Element);
}

Uygulama 3

while(scalar(@Array) !=0)
{
      $Element=shift(@Array);
      SubRoutine($Element);
}

Uygulama 4

for my $i (0 .. $#Array)
{
      SubRoutine($Array[$i]);
}

Uygulama 5

map { SubRoutine($_) } @Array ;

2
Neden bir "en iyisi" olsun? Özellikle birbirinizi nasıl ölçeceğiniz konusunda hiçbir fikrimiz olmadığına göre (hız hafıza kullanımından daha mı önemli? mapVe kabul edilebilir bir cevap mı? Vb.)
Max Lybbert

2
Gönderdiğiniz üç kişiden ikisi beni "WTH ?!" onları mantıklı alternatifler haline getirecek ek çevreleyen bağlam olmadıkça. Her durumda, bu soru " İki sayıyı toplamanın en iyi yolu nedir? " Düzeyindedir. Çoğu zaman tek bir yol vardır. Sonra, farklı bir yola ihtiyaç duyduğunuz durumlar vardır. Kapanış oylaması.
Sinan Ünür

4
@ SinanÜnür Sizin fikrinizle empati kuruyorum (iki sayıyı eklemenin tek bir yolu var), ancak analoji küçümseyecek kadar güçlü değil. Açıkçası, birden fazla yol var ve OP neyin iyi bir fikir olup neyin olmadığını anlamak istiyor.
CodeClown42

2
Programlama Perl'in üçüncü baskısının 24. Bölümü, iyi bir okuma olan verimlilik üzerine bir bölüme sahiptir. Zaman, programcı, bakımcı gibi farklı verimlilik türlerini ele alır. Bölüm, "Zamanı optimize etmenin bazen size uzay veya programcı verimliliğine mal olabileceğini unutmayın (aşağıdaki çelişkili ipuçlarıyla belirtilmiştir). Bunlar aralar."

1
İki sayı eklemenin bir 1 yolu? Daha düşük seviyeli çağrılara / uygulamalara bakarsanız değil ....
ileriye doğru

Yanıtlar:


76
  • Hız açısından: # 1 ve # 4, ancak çoğu durumda fazla değil.

    Doğrulamak için bir kıyaslama yazabilirsiniz, ancak # 1 ve # 4'ü biraz daha hızlı bulacağınızdan şüpheleniyorum çünkü yineleme çalışması Perl yerine C'de yapılıyor ve dizi öğelerinin gereksiz kopyalanması gerçekleşmiyor. ( $_Olup takma ad 1. eleman, ancak # 2 ve # 3, aslında kopya diziden skalarlar.)

    # 5 benzer olabilir.

  • Bellek kullanımı açısından: # 5 dışında hepsi aynı.

    for (@a)dizinin düzleşmesini önlemek için özel kasalıdır. Döngü, dizinin indeksleri üzerinde yinelenir.

  • Okunabilirlik açısından: # 1.

  • Esneklik açısından: # 1 / # 4 ve # 5.

    # 2, yanlış olan öğeleri desteklemez. # 2 ve # 3 yıkıcıdır.


3
Vay canına, kısa ve basit cümlelerle kamyonlarca bilgi ekledin.
jaypal singh

1
# 2, kuyruklar yaptığınızda iyidir (örneğin, önce my @todo = $root; while (@todo) { my $node = shift; ...; push @todo, ...; ...; }
enine

Uygulama 4, kullanılacak büyük miktarda bellek getirebilecek bir ara dizin dizisi yaratmıyor mu? Öyleyse, bu yaklaşımı kullanmamak gerekir gibi görünüyor. stackoverflow.com/questions/6440723/… rt.cpan.org/Public/Bug/Display.html?id=115863
Thorsten Schöning

@ikegami Şampiyon stilinize sadık - harika cevap :)
skeetastax

26

Yalnızca öğelerini önemsiyorsanız @Array, kullanın:

for my $el (@Array) {
# ...
}

veya

Endeksler önemliyse şunu kullanın:

for my $i (0 .. $#Array) {
# ...
}

Veya perl5.12.1'den itibaren şunları kullanabilirsiniz:

while (my ($i, $el) = each @Array) {
# ...
}

Döngünün gövdesinde hem öğeye hem de dizinine ihtiyacınız varsa, Beklemek isterim kullanma each en hızlı olmak, ama sonra5.12.1 öncesi ile uyumluluktan vazgeçeceksiniz perl.

Bunlardan başka bazı modeller belirli koşullar altında uygun olabilir.


eachEn yavaş olmasını beklerdim . Bir takma ad, artı bir liste ataması, iki skaler kopya ve iki skaler takas eksi diğerlerinin tüm işlerini yapar.
ikegami

Ve ölçüm yeteneğimin en iyisine göre, haklısın. forBir dizinin indisleri üzerinde yinelemeyle yaklaşık % 45 daha hızlı ve bir dizi referansının indeksleri üzerinde yinelemede% 20 daha hızlı ( $array->[$i]gövdeden erişiyorum ), eachile birlikte kullanmaya göre while.
Sinan Ünür

3

IMO, uygulama # 1 tipiktir ve Perl için kısa ve deyimsel olması, yalnızca bunun için diğerlerinden üstündür. Üç seçeneğin bir karşılaştırması, en azından size hız hakkında fikir verebilir.


2

1, diziyi dokunmadan bıraktığı için 2 ve 3'ten büyük ölçüde farklıdır, oysa diğer ikisi diziyi boş bırakır.

# 3'ün oldukça tuhaf ve muhtemelen daha az verimli olduğunu söyleyebilirim, bu yüzden unutun bunu.

Bu sizi # 1 ve # 2 olarak bırakır ve aynı şeyi yapmazlar, bu nedenle biri diğerinden "daha iyi" olamaz. Dizi büyüktür ve bunu tutmak gerek yoksa, genellikle bununla başa (edecek kapsam ancak bkz NOT ), bu yüzden genel olarak , 1. hala açık ve basit bir yöntemdir. Her bir öğeyi kaydırmak hiçbir şeyi hızlandırmaz. Diziyi referanstan serbest bırakma ihtiyacı olsa bile, şöyle yapardım:

undef @Array;

bittiğinde.

  • NOT : Dizinin kapsamını içeren alt yordam aslında diziyi korur ve bir dahaki sefere alanı yeniden kullanır. Genel olarak bu iyi olmalıdır (yorumlara bakın).

@Array = ();temeldeki diziyi serbest bırakmaz. Kapsam dışına çıkmak bile bunu yapmaz. Altta yatan diziyi serbest bırakmak istiyorsanız, kullanmalısınız undef @Array;.
ikegami

2
Demo; perl -MDevel::Peek -e'my @a; Dump(\@a,1); @a=qw( a b c ); Dump(\@a,1); @a=(); Dump(\@a,1); undef @a; Dump(\@a,1);' 2>&1 | grep ARRAY
ikegami

NE??? GC'nin tüm noktasının bir kez ref sayısı == 0 olduğunu düşünmüştüm, ilgili bellek geri dönüştürülebilir hale gelir.
CodeClown42

@ikegami: ()vs ile ilgili bir şey görüyorum undef, ancak kapsam dışına çıkmak, bu kapsamda yerel bir dizi tarafından kullanılan belleği serbest bırakmazsa, bu perl'i sızan bir felaket yapmaz mı? Bu doğru olamaz .
CodeClown42

Onlar da sızdırmazlar. Abone hala onların sahibidir ve bir dahaki sefere abone çağrıldığında onları yeniden kullanır. Hız için optimize edilmiştir.
ikegami

1

Öğeyi veya diziyi yazdırmak için tek satırda.

(@array) için $ _ yazdır;

NOT: $ _'ın dahili olarak döngüdeki @array öğesine başvurduğunu unutmayın. $ _ İçinde yapılan herhangi bir değişiklik @array'e yansır; ör.

my @array = qw( 1 2 3 );
for (@array) {
        $_ = $_ *2 ;
}
print "@array";

çıktı: 2 4 6


0

Karşılaştırmak için buna benzer sorulara karar vermenin en iyi yolu:

use strict;
use warnings;
use Benchmark qw(:all);

our @input_array = (0..1000);

my $a = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    foreach my $element (@array) {
       die unless $index == $element;
       $index++;
    }
};

my $b = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    while (defined(my $element = shift @array)) {
       die unless $index == $element;
       $index++;
    }
};

my $c = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    while (scalar(@array) !=0) {
       my $element = shift(@array);
       die unless $index == $element;
       $index++;
    }
};

my $d = sub {
    my @array = @{[ @input_array ]};
    foreach my $index (0.. $#array) {
       my $element = $array[$index];
       die unless $index == $element;
    }
};

my $e = sub {
    my @array = @{[ @input_array ]};
    for (my $index = 0; $index <= $#array; $index++) {
       my $element = $array[$index];
       die unless $index == $element;
    }
};

my $f = sub {
    my @array = @{[ @input_array ]};
    while (my ($index, $element) = each @array) {
       die unless $index == $element;
    }
};

my $count;
timethese($count, {
   '1' => $a,
   '2' => $b,
   '3' => $c,
   '4' => $d,
   '5' => $e,
   '6' => $f,
});

Ve bunu x86_64-linux-gnu-thread-multi için oluşturulmuş perl 5, version 24, subversion 1 (v5.24.1) üzerinde çalıştırmak

Alırım:

Benchmark: running 1, 2, 3, 4, 5, 6 for at least 3 CPU seconds...
         1:  3 wallclock secs ( 3.16 usr +  0.00 sys =  3.16 CPU) @ 12560.13/s (n=39690)
         2:  3 wallclock secs ( 3.18 usr +  0.00 sys =  3.18 CPU) @ 7828.30/s (n=24894)
         3:  3 wallclock secs ( 3.23 usr +  0.00 sys =  3.23 CPU) @ 6763.47/s (n=21846)
         4:  4 wallclock secs ( 3.15 usr +  0.00 sys =  3.15 CPU) @ 9596.83/s (n=30230)
         5:  4 wallclock secs ( 3.20 usr +  0.00 sys =  3.20 CPU) @ 6826.88/s (n=21846)
         6:  3 wallclock secs ( 3.12 usr +  0.00 sys =  3.12 CPU) @ 5653.53/s (n=17639)

Yani 'foreach (@Array)' diğerlerinden yaklaşık iki kat daha hızlıdır. Diğerleri çok benzer.

@ikegami, bu ifadelerde hız dışında epeyce farklılık olduğuna da dikkat çekiyor.


1
Karşılaştırma $index < $#arrayaslında olması gerektiği $index <= $#array, çünkü $#arraydizinin uzunluğu ama bunun son endeks değildir.
josch
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.