2009-08-26 1 views
5

Ich habe ein Perl-Skript, das foreach Schleifen nistet, wie unten zu sehen. Es dauert eine lange Zeit:Wie kann ich geschachtelte Schleifen verarbeiten, ohne foreach-Anweisungen in Perl zu verschachteln?

#! /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 Aussagen wie diese eine lange Zeit in Anspruch nimmt und es ist schwer zu lesen und nicht sehr hübsch. Kann jemand einen besseren Weg vorschlagen, diese Struktur mit Hashes oder einer anderen cleveren Struktur zu codieren?

+14

Es gibt kein Problem mit dem Code. Wenn Sie wirklich das tun wollen, was Sie geschrieben haben, dann ist dies der richtige Weg. Wenn Sie etwas anderes im Kopf haben, beschreiben Sie das. – jrockway

+1

Zum einen haben Sie Warnungen zweimal aktiviert. Entweder tun Sie es in der Shebang-Zeile ('#!/Usr/bin/perl -w') oder tun Sie es mit' use' - Sie müssen nicht beides tun. Ich bevorzuge "verwenden", weil es begrenzt ist, aber einige bevorzugen das "-w", vielleicht gerade weil es nicht ist, oder vielleicht, weil es kürzer ist. –

+0

@jrockway: Es funktioniert gut für mich, aber ich frage mich, ob ich einen besseren Code als das haben kann. – Space

Antwort

3

sehe ich nicht, was dein Problem ist, aber man könnte ein generisches Kartesisches Produkt verwenden, wenn Sie SQL oder etwas verwendet:

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

Ich könnte vorschlagen, dass Ihr kartesisches Sub Prototype ist, so dass Benutzer '\\' nicht vor allen ihren Listen verwenden müssen. Idealerweise sollten die Werte in der gleichen Reihenfolge ausgegeben werden, in der sie eingegeben wurden. –

+1

Bitte verwenden Sie auch "strict" und "warnings". –

+0

Wie würdest du es prototypieren? –

1

foreach ist vorzuziehen, da es lesbar ist. Was genau meinen Sie mit "jedes Array kann Probleme verursachen" (welche Probleme?) Und "Werte können nicht übereinstimmen" (welche Werte?)

+0

verschachtelte Foreachs sind ein Schmerz zu lesen und zu pflegen. Verwenden Sie eine Iteratorlösung wie Algorithm :: Loops oder Set :: CrossProduct. –

+0

Wenn Sie Ihren Kommentar betrachten, müsste er diese aus CPAN herunterladen. Ist es besser, integrierte Idiome wie foreach zu verwenden oder den Benutzer zum Herunterladen externer Pakete zu zwingen? Ich bevorzuge Lösungen, die so wenig externe Abhängigkeiten wie möglich haben. –

+0

Der Code für mein Modul ist minimal. Er konnte es sogar heben. Es ist überhaupt nicht vorzuziehen, foreach zu verwenden, da es seine Beschränkungen fest codiert. Andere Lösungen sind flexibel ohne die wiederholte Affe Arbeit, die Sie jedes Mal tun müssen, wenn Sie das Problem ändern. –

-1

Das einzige Problem, das ich möglicherweise bei der Verwendung von verschachtelten Schleifen habe, ist eine Mehrdeutigkeit in was $_ ist . Wenn man bedenkt, dass Sie es nicht einmal benutzen, glaube ich nicht, dass es einen besseren Weg gibt, zu tun, was Sie wollen.

Als Nebenbemerkung möchte ich hinzufügen, dass $_ in diesem Fall gut definiert ist, aber als Programmierer möchte ich vielleicht nicht mit dem Overhead der Erinnerung daran umgehen, worauf es bei jedem Schritt ankommt.

Haben Sie irgendwelche spezifischen betrifft mit dem Code?

+4

Uh, er benutzt $ _ nirgendwo. – jrockway

+0

Deshalb denke ich, dass er hier keine Probleme haben sollte. –

+2

-1: "Die einzige Sorge, die ich hier haben könnte, ist die Verwendung von Eiernudeln. Vielleicht möchten Sie nicht mit dem Aufwand, sich daran zu erinnern, was Eiernudeln mit Ihrem Code zu tun haben, beschäftigen. Aber da Sie es vermeiden, sollten Sie habe kein Problem mit diesem Code. " –

-5

Sie eine klassische for Schleife verwenden kann, statt.

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

Aber das hinterlässt immer noch die Probleme und Mismatchs, die Sie angesprochen haben. Ich schlage vor, dass Sie diese Probleme im do_functions Teil behandeln.

+3

Ich bin mir ziemlich sicher, dass Code das letzte Element jedes Arrays überspringen wird. Besser, den 'foreach'-Stil zu verwenden, damit Sie sich nicht mit Array-Indizes herumärgern müssen. –

+2

Ich verstehe nicht, warum Sie das tun würden Ich empfehle eine weniger idiomatische, ausführlichere "Lösung", die den Bewahrern eher wie eine mentale Geschwindigkeitsschwelle erscheint, und dann sagen "aber das löst dein Problem nicht." Wenn es ihr Problem nicht löst oder ihren Code in einigen verbessert Also, warum postest du eine Antwort? –

+1

Ich habe dies bearbeitet, um zumindest etwas korrekt zu sein. Nicht dass ich jemals <= in for loops ab 0 verwenden würde. $ i <@array ist viel einfacher zu lesen. – jrockway

2

Sie könnten einfach for verwenden.

(sorry, konnte nicht widerstehen)

+3

Ich war früher ein großer Befürworter von 'foreach' - ich dachte,' for' sollte für C-Style 'for' Loops sein, um das Lesen zu erleichtern für Betreuer ("Ist das eine Schleife im C-Stil oder Perl-Stil?"). Dann habe ich gemerkt, dass man in Perl fast nie die 'for'-Schleife im C-Stil braucht, und jetzt benutze ich sie ausschließlich. –

+7

Ich denke, JB bezog sich darauf, wie "for" und "foreach" Synonyme sind. Wenn der Fragesteller foreach nicht mag, kann er stattdessen "für" buchstabieren. –

+2

Nach meinem gelesen von Chris Lutz Kommentar verstand er, dass und er nur Bezug, dass er "for" für C-Style-Loops und "foreach" für Listen-Iteration reserviert, dann erkannte, dass er nie C-Style-Loops in Perl braucht, so verwendet er nun ausschließlich "für" exklusiv. (Und wenn das nicht seine Geschichte war, ist es sowieso meine.) –

1

Wenn ich Ihre Frage richtig verstehe, dann fragen Sie, wie Hashes zu verwenden, mit foreach Diskrepanzen zu vermeiden, dass Sie in Ihrem Array Beispiel haben würden ?.

Wenn ja dann ist hier ein Beispiel:

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"; 
     } 
    } 
} 


Sie können auch während & jeder verwenden, die auf dem Auge leichten Code macht produziert:

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

Beachten Sie auch, ich letzte Schleife entfernt im obigen Beispiel, weil es derzeit mit meinen Beispiel-Hash-Daten überflüssig ist.

NB. Wenn Sie vorhaben, Schlüssel zu ändern oder aus der Schleife auszubrechen, lesen Sie bitte each und wie es sich auf die Iteration auswirkt.

PS. In diesem Beispiel geht es nicht um die Schleife, sondern darum, dass Daten am besten als Hash und nicht als Array dargestellt werden. (obwohl es nicht 100% von der Frage klar ist, die so ist!).

6

Verwenden Sie meine Set::CrossProduct Modul oder Algorithm::Loops verwenden.Sie sollten keine hartcodierten verschachtelten Strukturen erstellen müssen, um mit diesen Problemen fertig zu werden. Beide Module können dies für eine beliebige Anzahl von Arrays tun.

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"; 
    } 

Nicht nur das, sondern die Cursor geben Ihnen Möglichkeiten im Iterator zu bewegen, so dass Sie sich auf die aktuelle Kombination nicht zu begrenzen. Sie können die vorherigen und nächsten Kombinationen überprüfen, die für Grenzen nützlich sein können (wie wenn das nächste Tupel ein anderer Server ist).

Achten Sie auf Leute, die alle Kombinationen im Speicher erstellen möchten. Das ist auch nicht nötig.

Verwandte Themen