Question

J'ai souvent un sous-programme en Perl qui remplit un tableau avec des informations.Comme je suis également habitué au hacking en C++, je me retrouve souvent à le faire ainsi en Perl, en utilisant des références :

my @array;
getInfo(\@array);

sub getInfo {
   my ($arrayRef) = @_;
   push @$arrayRef, "obama";
   # ...
}

au lieu de la version plus simple :

my @array = getInfo();

sub getInfo {
   my @array;
   push @array, "obama";
   # ...
   return @array;
}

La raison, bien sûr, est que je ne veux pas que le tableau soit créé localement dans le sous-programme puis copié au retour.

Est-ce correct?Ou est-ce que Perl optimise cela de toute façon ?

Était-ce utile?

La solution

Qu'en est-il de retourner une référence de tableau en premier lieu?

sub getInfo {
  my $array_ref = [];
  push @$array_ref, 'foo';
  # ...
  return $array_ref;
}

my $a_ref = getInfo();
# or if you want the array expanded
my @array = @{getInfo()};

Modifier selon le commentaire de dehmann:

Il est également possible d'utiliser un tableau normal dans la fonction et retourner une référence.

sub getInfo {
  my @array;
  push @array, 'foo';
  # ...
  return \@array;
}      

Autres conseils

Transmission de références est plus efficace, mais la différence est pas aussi grand que dans C ++. L'argument eux-mêmes valeurs (ce qui signifie: les valeurs du tableau) sont toujours passés par référence de toute façon (les valeurs renvoyées sont copiées bien)

.

La question est: est-ce important? La plupart du temps, il ne fonctionne pas. Si vous retournez 5 éléments, ne vous inquiétez pas. Si vous retournez / passant 100'000 éléments, références d'utilisation. optimiser seulement si elle est un goulot d'étranglement.

Si je regarde votre exemple et de réfléchir à ce que vous voulez faire, je suis habitué à l'écrire de cette manière:

sub getInfo {
  my @array;
  push @array, 'obama';
  # ...
  return \@array;
}

Il me semble que Version simple quand je dois retourner grande quantité de données. Il n'y a pas besoin de allouer array en dehors sub que vous écrit dans votre premier extrait de code parce que my le faire pour vous. Quoi qu'il en soit, vous ne devriez pas faire l'optimisation prématurée Leon Timmermans suggère .

Pour répondre à la rumination finale, non, Perl n'optimise pas loin. Il ne peut pas, vraiment, parce que le retour d'un tableau et en retournant un scalaire sont fondamentalement différentes.

Si vous avez affaire à de grandes quantités de données ou si la performance est une préoccupation majeure, alors vos habitudes C vous servira bien - passer et retourner des références à des structures de données plutôt que les structures elles-mêmes afin qu'ils auront pas besoin de copier. Mais, comme Leon Timmermans a souligné, la grande majorité du temps, vous avez affaire à de petites quantités de données et les performances ne sont pas un gros problème, donc le faire de quelque manière semble plus lisible.

Ceci est la façon dont je retournerais normalement un tableau.

sub getInfo {
  my @array;
  push @array, 'foo';
  # ...
  return @array if wantarray;
  return \@array;
}

De cette façon, il fonctionnera de la façon dont vous voulez, dans scalaire, ou des contextes de liste.

my $array = getInfo;
my @array = getInfo;

$array->[0] == $array[0];

# same length
@$array == @array;

Je ne voudrais pas essayer de l'optimiser à moins que vous le savez est une partie lente de votre code. Même alors j'utiliser des repères pour voir quel sous-programme est plus rapide.

Il y a deux considérations.La question la plus évidente est de savoir quelle sera la taille de votre réseau ?S'il s'agit de moins de quelques dizaines d'éléments, alors la taille n'est pas un facteur (sauf si vous effectuez une micro-optimisation pour une fonction appelée rapidement, mais vous devrez d'abord effectuer un profilage de la mémoire pour le prouver).

C'est la partie la plus facile.La deuxième considération souvent négligée est l’interface.Comment le tableau renvoyé sera-t-il utilisé ?Ceci est important car le déréférencement d’un tableau entier est plutôt horrible en Perl.Par exemple:

for my $info (@{ getInfo($some, $args) }) {
    ...
}

C'est moche.Ceci est vraiment mieux.

for my $info ( getInfo($some, $args) ) {
    ...
}

Il se prête également à la cartographie et au grappage.

my @info = grep { ... } getInfo($some, $args);

Mais renvoyer une référence de tableau peut être pratique si vous souhaitez sélectionner des éléments individuels :

my $address = getInfo($some, $args)->[2];

C'est plus simple que :

my $address = (getInfo($some, $args))[2];

Ou:

my @info = getInfo($some, $args);
my $address = $info[2];

Mais à ce stade, vous devriez vous demander si @info est vraiment une liste ou un hachage.

my $address = getInfo($some, $args)->{address};

Ce que vous ne devriez pas faire, c'est avoir getInfo() renvoie une référence de tableau dans un contexte scalaire et un tableau dans un contexte de liste.Cela brouille l'utilisation traditionnelle du contexte scalaire comme longueur de tableau, ce qui surprendra l'utilisateur.

Enfin, je vais brancher mon propre module, Méthode ::Signatures, car il offre un compromis pour transmettre des références de tableau sans avoir à utiliser la syntaxe array ref.

use Method::Signatures;

method foo(\@args) {
    print "@args";      # @args is not a copy
    push @args, 42;   # this alters the caller array
}

my @nums = (1,2,3);
Class->foo(\@nums);   # prints 1 2 3
print "@nums";        # prints 1 2 3 42

Cela se fait grâce à la magie de Données : Alias.

3 autres améliorations de performances potentiellement importants si vous lisez un entier, et le fichier largish couper dans un tableau:

  1. Désactiver TAMPON avec (manuel met en garde contre sysread () au lieu de lecture () A propos de mélange)
  2. Pré-étendre le tableau en valorisant le dernier élément -     Savès allocations de mémoire
  3. Utilisation Décompresser () pour diviser les données rapidement comme données de canal graphique uint16_t

En passant une référence à un tableau à la fonction permet au programme principal de traiter avec un simple tableau tandis que l'écriture unique et oublier la fonction de travailleur utilise le plus compliqué « $ @ pour » et flèche -> [II $] formes d'accès. Être tout à fait C'ish, il est susceptible d'être rapide!

Je ne sais rien à propos de Perl c'est donc une réponse linguistique neutre.

Il est, dans un sens, inefficace pour copier un tableau à partir d'un sous-programme dans le programme appelant. L'inefficacité se pose dans la mémoire supplémentaire utilisée et le temps nécessaire pour copier les données d'un endroit à l'autre. D'autre part, pour tous, mais les plus grands tableaux, vous pourriez ne pas donner une putain, et peut-être préférer copier des tableaux pour l'élégance, cussedness ou toute autre raison.

La solution efficace est pour le sous-programme pour passer le programme appelant l'adresse du tableau. Comme je le dis, je n'ai pas la moindre idée sur le comportement par défaut de Perl à cet égard. Mais certaines langues fournissent au programmeur la possibilité de choisir l'approche.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top