Domanda

Spesso ho una subroutine in Perl che riempie un array con alcune informazioni.Dato che sono abituato a hackerare anche in C++, mi ritrovo spesso a farlo in questo modo in Perl, usando i riferimenti:

my @array;
getInfo(\@array);

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

invece della versione più semplice:

my @array = getInfo();

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

Il motivo, ovviamente, è che non voglio che l'array venga creato localmente nella subroutine e quindi copiato al ritorno.

È giusto?O Perl lo ottimizza comunque?

È stato utile?

Soluzione

Che dire di restituire un riferimento ad array, in primo luogo?

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

Edit in base al commento di dehmann:

E 'anche possibile utilizzare un array normale nella funzione e restituire un riferimento ad esso.

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

Altri suggerimenti

riferimenti scomparsa è più efficiente, ma la differenza non è così grande come in C ++. L'argomento stessi valori (che significa: i valori nella matrice) vengono sempre passati per riferimento comunque (valori restituiti vengono copiati però)

.

La domanda è: che importa? La maggior parte del tempo, non è così. Se stai tornando 5 elementi, non si preoccupano a questo proposito. Se stai tornando / passando 100'000 elementi, utilizzare i riferimenti. Solo ottimizzarlo se si tratta di un collo di bottiglia.

Se guardo il tuo esempio e pensare a ciò che si vuole fare sono abituato a scrivere in questo modo:

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

Mi sembra come Versione lineare quando ho bisogno di tornare grandi quantità di dati. Non c'è bisogno di allocare array fuori sub come scritto nel tuo primo frammento di codice perché my farlo per voi. In ogni caso non si deve fare l'ottimizzazione prematura come Leon Timmermans suggeriscono .

Per rispondere alla ruminazione finale, no, Perl non ottimizzare questa via. Esso non può, in realtà, perché restituendo un array e restituendo uno scalare sono fondamentalmente diversi.

Se hai a che fare con grandi quantità di dati o se le prestazioni sono una grande preoccupazione, quindi le vostre abitudini C vi servirà bene - pass e restituire i riferimenti a strutture dati, piuttosto che le strutture stesse in modo che essi non avranno bisogno di copiare. Ma, come Leon Timmermans ha sottolineato, la stragrande maggioranza del tempo, hai a che fare con piccole quantità di dati e le prestazioni non è un grosso problema, in modo da farlo in qualunque modo sembra più leggibile.

Questo è il modo che normalmente restituire un array.

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

In questo modo funzionerà nel modo desiderato, a scalare o lista contesti.

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

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

# same length
@$array == @array;

Non vorrei cercare di ottimizzare a meno che non si sa che è una parte lenta del codice. Anche allora vorrei usare punti di riferimento per vedere quale subroutine è effettivamente più veloce.

Ci sono due considerazioni.Quello ovvio è: quanto sarà grande il tuo array?Se si tratta di meno di poche dozzine di elementi, la dimensione non è un fattore (a meno che non si stia ottimizzando micro per qualche funzione chiamata rapidamente, ma prima si dovrebbe eseguire un po' di profilazione della memoria per dimostrarlo).

Questa è la parte facile.La seconda considerazione spesso trascurata è l’interfaccia.Come verrà utilizzato l'array restituito?Questo è importante perché il dereferenziamento dell'intero array è piuttosto orribile in Perl.Per esempio:

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

È brutto.Questo è molto meglio.

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

Si presta anche al mapping e al grepping.

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

Ma restituire un riferimento all'array può essere utile se intendi selezionare singoli elementi:

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

È più semplice di:

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

O:

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

Ma a quel punto dovresti chiederti se @info è davvero una lista o un hash.

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

Ciò che non dovresti fare è avere getInfo() restituisce un array ref in contesto scalare e un array in contesto elenco.Ciò confonde l'uso tradizionale del contesto scalare come lunghezza dell'array, il che sorprenderà l'utente.

Infine, collegherò il mio modulo, Metodo::Firme, perché offre un compromesso per passare i riferimenti all'array senza dover utilizzare la sintassi dell'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

Questo viene fatto attraverso la magia di Dati::Alias.

3 altri miglioramenti delle prestazioni potenzialmente elevato se si sta leggendo un intero, un file abbastanza grande ed affettare in un array:

  1. Spegnere BUFFERING con sysread () invece di read () (manuale mette in guardia A proposito di miscelazione)
  2. Pre-estendere la matrice valorizzando l'ultimo elemento -     salva le allocazioni di memoria
  3. Usa Spacchettare () per i dati, come i dati del canale grafica uint16_t diviso rapidamente

Passaggio di un riferimento ad array alla funzione permette al programma principale per affrontare con un semplice array mentre la funzione write-once-and-forget lavoratore utilizza la più complicata "$ @" e la freccia -> [$ II] accesso forme. Essendo abbastanza C'ish, è probabile che sia veloce!

Non so nulla di Perl quindi questa è una risposta dalla lingua.

Si tratta, in un certo senso, inefficiente per copiare una matrice da una subroutine nel programma chiamante. L'inefficienza si pone nella memoria più utilizzato e il tempo necessario per copiare i dati da un luogo ad un altro. D'altra parte, per tutti, ma i più grandi array, si potrebbe non me ne frega niente, e potrebbe preferire di copiare gli array per eleganza, cussedness o qualsiasi altro motivo.

La soluzione è efficace per la subroutine per passare il programma chiamante l'indirizzo della matrice. Come ho detto, io non ho un indizio circa il comportamento predefinito di Perl a questo riguardo. Ma alcuni linguaggi forniscono al programmatore la possibilità di scegliere quale approccio.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top