112 votes

Meilleure façon d'itérer dans un tableau Perl

Quelle est la meilleure implémentation (en termes de vitesse et d'utilisation de la mémoire) pour itérer dans un tableau Perl ? Existe-t-il une meilleure méthode ? ( @Array ne doit pas être retenu).

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 ;

2 votes

Pourquoi y aurait-il un "meilleur" ? D'autant plus que nous n'avons aucune idée de la manière dont vous pourriez mesurer l'un par rapport à l'autre (la vitesse est-elle plus importante que l'utilisation de la mémoire ? map et réponse acceptable ?. etc.)

2 votes

Deux des trois que vous avez affichés me feraient dire "WTH ?!" à moins qu'il n'y ait un contexte environnant supplémentaire pour en faire des alternatives raisonnables. Dans tous les cas, cette question se situe au niveau de " Quelle est la meilleure façon d'additionner deux nombres ? " La plupart du temps, il n'y a qu'une seule solution. Puis, il y a ces circonstances, où vous avez besoin d'un moyen différent. Vote de clôture.

4 votes

@SinanÜnür Je comprends votre opinion (qu'il n'y a qu'une seule façon d'additionner deux nombres), mais l'analogie n'est pas assez forte pour être utilisée avec dédain. De toute évidence, il y a plus d'une façon de faire, et le PO veut comprendre ce qui est une bonne idée et ce qui ne l'est pas.

93voto

ikegami Points 133140
  • En termes de vitesse : #1 et #4, mais pas de beaucoup dans la plupart des cas.

    Vous pourriez écrire un benchmark pour confirmer, mais je pense que vous trouverez les numéros 1 et 4 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 n'a lieu. ( $_ es alias à l'élément dans #1, mais #2 et #3 en fait copie les scalaires du tableau).

    Le numéro 5 pourrait être similaire.

  • En termes d'utilisation de la mémoire : ils sont tous pareils, sauf le numéro 5.

    for (@a) est un cas particulier pour éviter d'aplatir le tableau. La boucle itère sur les indices du tableau.

  • En termes de lisibilité : #1.

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

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

10 votes

Wow, vous avez ajouté des tonnes d'informations dans des phrases courtes et simples.

2 votes

Le n° 2 est bon lorsque vous faites des files d'attente (par exemple, des recherches en largeur d'abord) : my @todo = $root; while (@todo) { my $node = shift; ...; push @todo, ...; ...; }

0 votes

L'implémentation 4 ne crée-t-elle pas un tableau intermédiaire d'indices, ce qui pourrait introduire une grande quantité de mémoire à utiliser ? Si c'est le cas, il semble que l'on ne devrait pas utiliser cette approche. stackoverflow.com/questions/6440723/ rt.cpan.org/Public/Bug/Display.html?id=115863

33voto

Sinan Ünür Points 76179

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

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

ou

Si les indices sont importants, utilisez :

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

Ou, à partir de 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 pour être le plus rapide, mais ensuite vous abandonnez la compatibilité avec les versions antérieures à la 5.12.1. perl s.

Un autre modèle que ceux-ci pourrait être approprié dans certaines circonstances.

0 votes

Je m'attendrais à ce que le each pour être le plus lent. Il fait tout le travail des autres moins un alias, plus une affectation de liste, deux copies scalaires et deux effacements scalaires.

1 votes

Et, au mieux de ma capacité de mesure, vous avez raison. Environ 45% plus rapide avec for en itérant sur les indices d'un tableau, et 20% plus rapide en itérant sur les indices d'une référence de tableau (je fais l'accès à $array->[$i] dans le corps), sur l'utilisation each en collaboration avec while .

4voto

La meilleure façon de décider de ce genre de questions est de les évaluer :

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 en exécutant ceci sur perl 5, version 24, subversion 1 (v5.24.1) construit pour x86_64-linux-gnu-thread-multi

J'ai compris :

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)

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

@ikegami souligne également qu'il existe un certain nombre de différences dans ces implémentations autres que la vitesse.

4voto

JRFerguson Points 4628

Selon moi, l'implémentation n° 1 est typique et, étant courte et idiomatique pour Perl, elle l'emporte sur les autres pour cette seule raison. Un benchmark des trois choix pourrait vous donner un aperçu de la vitesse, au moins.

3voto

Sandeep_black Points 647

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

imprimer $_ for (@array) ;

REMARQUE : n'oubliez pas que $_ fait référence en interne à l'élément de @array dans la boucle. Toute modification apportée à $_ sera répercutée dans @array ; ex.

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

sortie : 2 4 6

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X