2009-08-26 7 views
5

Ho uno script Perl che annida i cicli foreach come mostrato di seguito. Ci vuole molto tempo:Come posso gestire il ciclo annidato senza annidare le istruzioni foreach in Perl?

#! /usr/bin/perl 

use strict; 
use warnings; 

my @sites = ('a', 'b', 'c'); 
my @servers = ('A', 'B'); 
my @data_type = ("X", "Y", "Z"); 

foreach my $site (@sites) { 
    foreach my $server_type (@servers) { 
     foreach my $data (@data_type) { 
      #statements 
     } 
    } 
} 

Nesting foreach dichiarazioni come questo richiede molto tempo ed è difficile da leggere e non molto carina. Qualcuno può suggerire un modo migliore per codificare questa struttura usando gli hash o qualche altra struttura intelligente?

+14

Nessun problema con il codice. Se vuoi davvero fare ciò che hai scritto, allora questo è il modo per farlo. Se hai in mente qualcos'altro, descriverlo. – jrockway

+1

Per prima cosa, hai impostato due volte gli avvisi. O farlo nella riga shebang ('#!/Usr/bin/perl -w') o farlo con' use' - non è necessario fare entrambe le cose. Io preferisco "usare" perché ha un ambito, ma alcuni preferiscono il "-w", forse proprio perché non è "ambito", o forse perché è più corto. –

+0

@jrockway: Funziona bene per me, ma mi chiedo solo se posso avere un codice migliore di questo. – Space

risposta

3

non vedo che cosa il vostro problema è, ma è possibile utilizzare un prodotto cartesiano generica se siete abituati a SQL o qualcosa del genere:

sub cartesian { 
    my @C = map { [ $_ ] } @{ shift @_ }; 
    foreach (@_) { 
     my @A = @$_; 
     @C = map { my $n = $_; map { [ $n, @$_ ] } @C } @A; 
    } 
    return @C; 
} 

my @sites = ('a', 'b', 'c'); 
my @servers = ('A', 'B'); 
my @data_type = ("X", "Y", "Z"); 

foreach (cartesian(\@sites, \@servers, \@data_type)) { 
    ($data, $server_type, $site) = @$_; 
    print "$site $server_type $data\n"; 
} 
+0

Potrei suggerire che il tuo sotto cartesiano sia prototipato, in modo che gli utenti non debbano usare '\\' prima di tutti i loro elenchi. Inoltre, idealmente dovrebbe sputare i valori nello stesso ordine in cui sono stati inseriti. –

+1

Inoltre, per favore 'usa strict' e' warnings'. –

+0

Come lo si prototipo? –

1

foreach è preferibile perché è leggibile. Che cosa intendi esattamente per "ogni array può causare problemi" (quali problemi?) E "i valori possono non corrispondere" (quali valori?)

+0

foreach annidati sono un dolore da leggere e mantenere. Utilizzare una soluzione iteratore come Algorithm :: Loops o Set :: CrossProduct. –

+0

Guardando il tuo commento, dovrebbe scaricare quelli da CPAN. È meglio usare gli idiomi predefiniti come foreach o forzare l'utente a scaricare pacchetti esterni? Preferisco le soluzioni che hanno il minor numero di dipendenze esterne possibili. –

+0

Il codice per il mio modulo è minimo. Potrebbe persino sollevarlo. Non è affatto preferibile utilizzare foreach poiché è difficile codificarne i limiti. Altre soluzioni sono flessibili senza il lavoro ripetuto delle scimmie che devi fare ogni volta che cambi il problema. –

-1

L'unica preoccupazione che posso avere quando si utilizzano i cicli annidati è una certa ambiguità in quello che è $_ . Considerando che non lo stai nemmeno usando, non penso che ci sia un modo migliore per fare quello che vuoi.

Come sidenote, vorrei aggiungere che $_ è ben definito in questo caso, ma come programmatore potrei non voler affrontare il sovraccarico di ricordare a che punto si riferisce ad ogni passaggio.

Hai qualche problema con lo specifico?

+4

Uh, non sta usando $ _ ovunque. – jrockway

+0

Ecco perché penso che non dovrebbe avere problemi qui. –

+2

-1: "L'unica preoccupazione che potrei avere qui è l'uso di tagliatelle all'uovo.Potresti non occuparti del sovraccarico di ricordare cosa hanno a che fare i noodles all'uovo con il tuo codice.Tuttavia, poiché non utilizzi il tuo codice, dovresti non avere alcun problema con questo codice. " –

-5

È possibile utilizzare un classico for ciclo, invece.

for(my $i = 0; $i <= $#sites; $i++){ 
    for(my $j = 0; $j <= $#servers; $j++){ 
     for(my $k = 0; $k <= $#data_type; $k++){ 
      do_functions ... 

Ma questo lascia ancora i problemi e le discrepanze a cui si stava rivolgendo. Ti suggerisco di gestire questi problemi nella parte do_functions.

+3

Sono abbastanza sicuro che il codice salterà l'ultimo elemento di ogni array.Meglio usare lo stile 'foreach', quindi non c'è bisogno di scherzare con gli indici di array. –

+2

Non capisco perché tu voglia raccomandare una "soluzione" meno idiomatica, più verbosa, che è più simile a un picco di velocità mentale per i manutentori, e quindi dire "ma questo non risolve il problema". Se non risolve il problema, o migliora il loro codice in alcuni modo, perché stai postando una risposta? –

+1

Ho modificato questo almeno alquanto corretto.Non avrei mai usato <= in per cicli a partire da 0. $ i <@array è molto più facile da leggere. – jrockway

2

Si potrebbe semplicemente utilizzare for.

(mi dispiace, non poteva resistere)

+3

Ero un grande sostenitore di 'foreach' - pensavo che l'uso di' for' dovrebbe essere per i cicli 'for' in C-style, per renderlo più facile da leggere per manutentori ("Si tratta di un ciclo in stile C o in stile Perl?"). Poi ho capito che, in Perl, non hai quasi mai bisogno del ciclo 'for' in stile C, e ora lo uso esclusivamente. –

+7

Penso che JB si riferisse a come "for" e "foreach" sono sinonimi. Se il richiedente non gli piace, può scegliere di scriverlo "per" invece. –

+2

A giudicare dalla mia lettura del commento di Chris Lutz, ha capito che e stava solo relazionando che aveva usato "for" per i loop in stile C e "foreach" per l'iterazione delle liste, poi si rese conto che non aveva mai bisogno di loop in C in Perl, quindi ora usa felicemente "per" esclusivamente. (E se quella non era la sua storia, è comunque mia.) –

1

Se ho capito la tua domanda correttamente allora si chiede come utilizzare hash con foreach per evitare discordanze che si avrebbe nel tuo esempio di matrice ?.

Se è così allora qui è un esempio:

use strict; 
use warnings; 

my %sites = (

    a => { 
     A => { 
      data_type => [ 'X', 'Y' ], 
     } 
    }, 

    b => { 
     B => { 
      data_type => [ 'Y', 'Z' ], 
     } 
    }, 

    c => { 

    }, 
); 

for my $site (keys %sites) { 
    for my $server (keys %{ $sites{ $site } }) { 
     for my $data (keys %{ $sites{ $site }{ $server } }) { 
      my @data_types = @{ $sites{ $site }{ $server }{ data_type } }; 
      say "On site $site is server $server with $data @data_types"; 
     } 
    } 
} 


È anche possibile utilizzare mentre & ciascuno che fa produce codice più facile per gli occhi:

while (my ($site, $site_info) = each %sites) { 
    while (my ($server, $server_info) = each %{ $site_info }) { 
     my @data_types = @{ $server_info->{data_type} }; 
     say "On site $site we have server $server with data types @data_types" 
      if @data_types; 
    } 
} 

noti inoltre ho rimosso ultimo ciclo nell'esempio sopra perché è attualmente superfluo con i miei dati hash di esempio.

NB. Se hai intenzione di modificare le chiavi o interrompere il ciclo, ti preghiamo di leggere su each e come influenza l'iterazione.

PS. Questo esempio non riguarda il ciclo, ma i dati vengono rappresentati al meglio come Hash e non come Array! (anche se non è chiaro al 100% dalla domanda che è così!).

6

Utilizzare il modulo Set::CrossProduct o utilizzare Algorithm::Loops.Non dovresti creare strutture nidificate e codificate per affrontare questi problemi. Entrambi i moduli possono farlo per te per un numero arbitrario di array.

use Set::CrossProduct; 

my @sites = ('a', 'b', 'c'); 
my @servers = ('A', 'B'); 
my @data_type = ("X", "Y", "Z"); 

my $cross = Set::CrossProduct->new( 
    [ \@sites, \@servers, \@data_type ] 
    ); 

while(my $tuple = $cross->get) { 
    print "@$tuple\n"; 
    } 

Non solo, ma il cursore si dà modi per spostarsi nella iteratore in modo da non è necessario limitarsi alla combinazione corrente. È possibile esaminare le combinazioni precedenti e successive, che potrebbero essere utili per i limiti (come quando la tupla successiva è un server diverso).

Attenzione per le persone che desiderano creare tutte le combinazioni in memoria. Non c'è nemmeno bisogno di farlo.