web-dev-qa-db-fra.com

Meilleure façon de parcourir un tableau Perl

Quelle est la meilleure implémentation (en termes de vitesse et d’utilisation de la mémoire) pour une itération dans un tableau Perl? Y a-t-il une meilleure façon? (@Array n'a pas besoin d'être conservé).

Mise en œuvre 1

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

Mise en œuvre 2

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

Mise en œuvre 3

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

Mise en œuvre 4

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

Mise en œuvre 5

map { SubRoutine($_) } @Array ;
88
Jean
  • En termes de vitesse: n ° 1 et n ° 4, mais pas beaucoup dans la plupart des cas.

    Vous pouvez écrire un point de repère pour confirmer, mais je suppose que vous constaterez que les n ° 1 et 4 sont légèrement plus rapides, car le travail d'itération est effectué en C au lieu de Perl, et aucune copie inutile des éléments du tableau ne se produit. ($_ est aliasé de l'élément dans # 1, mais # 2 et # 3 réellement copie les scalaires du tableau.)

    # 5 pourrait être similaire.

  • En termes d’utilisation de la mémoire: ils sont tous identiques, à l’exception du n ° 5.

    for (@a) est spécialement conçu pour éviter d’aplatir le tableau. La boucle itère sur les index du tableau.

  • En termes de lisibilité: # 1.

  • En termes de flexibilité: n ° 1/n ° 4 et n ° 5.

    # 2 ne supporte pas les éléments qui sont faux. Les n ° 2 et n ° 3 sont destructeurs.

67
ikegami

Si vous ne vous souciez que des éléments de @Array, utilisez:

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

ou 

Si les indices sont importants, utilisez:

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

Ou, à partir de la version Perl 5.12.1, vous pouvez utiliser:

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

Si vous avez besoin à la fois de l'élément et de son index dans le corps de la boucle, Je m'attendrais en utilisant each être le plus rapide, mais alors vous abandonnerez la compatibilité avec les Perls antérieures à la 5.12.1.

Une autre configuration que celles-ci pourrait être appropriée dans certaines circonstances.

23
Sinan Ünür

OMI, la mise en œuvre n ° 1 est typique et être court et idiomatique pour Perl l'emporte sur les autres pour cela uniquement. Un repère des trois choix pourrait vous donner un aperçu de la vitesse, au moins.

3
JRFerguson

1 est sensiblement différent de 2 et 3, car il laisse le tableau intact, alors que les deux autres le laissent vide.

Je dirais que le n ° 3 est assez farfelu et probablement moins efficace, alors oubliez ça.

Ce qui vous laisse avec # 1 et # 2, et ils ne font pas la même chose, donc on ne peut pas être "meilleur" que l'autre. Si le tableau est grand et que vous n'avez pas besoin de le conserver, la portée de généralement sera traitée (mais voirNOTE), donc généralement, # 1 reste la méthode la plus claire et la plus simple. Décaler chaque élément n'accélère rien. Même s'il est nécessaire de libérer le tableau de la référence, je voudrais simplement aller:

undef @Array;

lorsque vous avez terminé.

  • NOTE: Le sous-programme contenant la portée du tableau conserve le tableau et réutilise l'espace la prochaine fois. En général, ça devrait aller (voir commentaires).

En une seule ligne pour imprimer l'élément ou le tableau. 

print $ _ for (@array);

NOTE: rappelez-vous que $ _ fait référence en interne à l'élément de @array en boucle. Toute modification apportée dans $ _ sera reflétée dans @array; ex. 

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

sortie: 2 4 6 

0
Sandeep_black

La meilleure façon de décider de telles questions pour les comparer:

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

Et ceci sur Perl 5, version 24, Subversion 1 (v5.24.1) construit pour x86_64-linux-gnu-thread-multi

Je reçois:

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)

Donc, 'foreach (@Array)' est environ deux fois plus rapide que les autres. Tous les autres sont très similaires.

@ikegami fait également remarquer qu'il existe de nombreuses différences dans ces implémentations autres que la vitesse.

0
G. Allen Morris III