2013-08-06 14 views
7

Inizialmente, sto lavorando con una lista con lunghezza = 2^16. Tuttavia, per astrarre questo, imposterò length = 5 in questo esempio.Come ottenere un elenco per mostrare gli zeri per le voci vuote in Perl?

#subroutine to make undefined entries -> 0 
sub zeros { 
    foreach(@_) { 
     if(!defined($_)) { 
      $_ = 0; 
     } 
    } 
}  
#print out and indicies and elements of list 
    sub checking { 
     print "List = \n"; 
     my $counter = 0; 
     foreach (@_) { 
      print "index = $counter\n"; 
      print "$_\n"; 
      $counter += 1; 
     } 
      print "\n"; 
    } 

Metodo 1: Se accedo indici diversi per modificare l'elemento, ricevo il seguente quando stampo le matrici. Non voglio vedere in bianco. Voglio che siano 0. Ho già impostato una subroutine "zeri" per fare diventare zero le voci non definite. Ma non so cosa sia andato storto nel mio codice. Ho anche provato "$ _ + = 0" per ogni elemento della lista. Ancora non riuscivo a ottenere zeri per le voci vuote.

#method 1 
@abc =(); 
$abc[1] = 3; 
$abc[5] = 5; 
&zeros(@abc); 
&checking(@abc); 
List = 
index = 0 

index = 1 
3 
index = 2 

index = 3 

index = 4 

index = 5 
5 

E Metodo 2: posso ottenere zeri se inizializzare l'elenco come questo. Ma come ho detto, sto lavorando con una lista molto lunga, non posso assolutamente inizializzare la mia lista come questa.

#method 2 
@abc = (3,0,0,0,5); 
&checking(@abc); 

List = 
index = 0 
3 
index = 1 
0 
index = 2 
0 
index = 3 
0 
index = 4 
5 
+0

È possibile inizializzare l'elenco utilizzando '@abc = (0) x 2 ** 16' che lo imposta su un elenco di 2 ** 16 zeri? –

+2

Si dovrebbe sempre usare 'use strict; usare avvertenze; ​​'. Renderà la tua vita molto più facile. – TLP

risposta

4

Il tuo approccio è corretto, ma c'è un problema con il funzionamento zeros(). Lo chiami con @abc come parametro, che crea una copia di quell'elenco. Quindi cambia la copia. Alla fine del sub, quella copia viene scartata. Nella tua funzione checking(), stai ancora utilizzando l'elenco originale.

È possibile risolvere il problema in questo modo:

sub zeros { 
    my @list = @_; 
    @list = map { $_ // 0 } @list; 
    return @list; 
} 

@abc = zeros(@abc); 
checking(@abc); 

Il trucco è quello di restituire l'elenco alterati e riassegnare alla variabile originale.

Se avessi usato strict e warnings che ti avrebbe raccontato che:

Use of uninitialized value $_ in concatenation (.) or string at F:\scratch.pl line 28. List = index = 0 

index = 1 3 index = 2 

index = 3 

index = 4 

index = 5 5 

Use of uninitialized value $_ in concatenation (.) or string at F:\scratch.pl line 28. 
Use of uninitialized value $_ in concatenation (.) or string at F:\scratch.pl line 28. 
Use of uninitialized value $_ in concatenation (.) or string at F:\scratch.pl line 28. 

Ma dal momento che si tratta di un grande array, vorrei consigliare di utilizzare un riferimento ad array, invece, perché la copia sarà costosa.

sub zeros { 
    $_ //= 0 for @{ $_[0] }; 
} 

zeros(\@abc); 
checking(@abc); 
+0

+1 per usare '//' e '// =' –

+1

Attenzione: nessuna copia da quella lista viene creata quando la si passa come argomento. Gli elementi originali sono alias. – sidyll

+0

@sidyll: Se questo è il caso, perché il sub 'zero' originale dell'OP non funziona? Dovrebbe alterare gli alias, non dovrebbe? – simbabque

4

Puoi inizializzare il vostro elenco utilizzando

@abc = (0) x 2**16 

che imposta a un elenco di 2 ** 16 zeri?

Ho provato a utilizzare il metodo degli zeri. Ha funzionato se inizializzare l'allineamento in questo modo:

@abc = (undef, 1, undef, undef, undef, 5) 

Quindi sembra che la subroutine non sostituisce array voci che non esistono (al contrario di esistente, ma con un valore di undef)

Nel qual caso si potrebbe provare estendere la zeros subroutine per restituire l'array modificato e assegnare tale indietro alla matrice originale:

#subroutine to make undefined entries -> 0 
sub zeros { 
    foreach(@_) { 
     if(!defined($_)) { 
      $_ = 0; 
     } 
    } 
    return @_; 
}  

@abc =(); 
$abc[1] = 3; 
$abc[5] = 5; 
@abc = zeros(@abc); 
# Check: 
print "index = $_\n$abc[$_]\n" for 0..$#abc; 

In alternativa, è possibile passare un riferimento alla matrice originale:

#subroutine to make undefined entries -> 0 
sub zeroref { 
    my ($array) = @_; # Expect a single argument: An array-reference 
    foreach(@$array) { 
     if(!defined($_)) { 
      $_ = 0; 
     } 
    } 
}  

@abc =(); 
$abc[1] = 3; 
$abc[5] = 5; 
zeroref(\@abc); # Pass an array-reference instead 
# Check: 
print "index = $_\n$abc[$_]\n" for 0..$#abc; 
1
use 5.010; 
$_ //= 0 for @abc; 

Per pre 5.10 Perls,

$_ = defined($_) ? $_ : 0 for @abc; 

Se si vuole trasformarlo in una funzione, non restituiscono un valore come la sostituzione è sul posto:

use 5.014; 
use strict; 
use warnings; 
use YAML; 

my @abc; 
$abc[1] = 3; 
$abc[5] = 5; 

print Dump \@abc; 

map_undefined_entries_to_zeros(\@abc); 

print Dump \@abc; 

sub map_undefined_entries_to_zeros { 
    my $array_ref = shift; 
    $_ = defined($_) ? $_ : 0 for @$array_ref; 
    return; 
} 
1

Se preferisci la sostituzione lazy o on-demand di elementi non defini con zeri, puoi tie la matrice in questo modo:

package Ad::Hoc::UndefToZero; 

use Tie::Array; 
our @ISA = qw(Tie::StdArray); 

sub FETCH { 
    my ($tied, $i) = @_; 
    my $elt = $tied->SUPER::FETCH($i); 
    unless (defined $elt) { 
    $elt = 0; 
    $tied->STORE($i, $elt); # $tiedarray[$i] is now zero 
    } 
    $elt; 
} 

package main; 

tie my @abc, 'Ad::Hoc::UndefToZero'; 
$abc[1] = 3; 
$abc[5] = 5; 

print "$abc[0]\n"; # $abc[0] now set to zero 
Problemi correlati