2009-08-26 67 views
5

Tengo un script Perl que anida los bucles foreach como se ve a continuación. Se tarda mucho tiempo:¿Cómo puedo manejar el bucle anidado sin anidar las instrucciones foreach en 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 
     } 
    } 
} 

foreach declaraciones de anidamiento como esto lleva mucho tiempo y es difícil de leer y no muy bonita. ¿Alguien puede sugerir una mejor manera de codificar esta estructura usando hash o alguna otra estructura inteligente?

+14

No hay problema con el código. Si realmente quieres hacer lo que has escrito, entonces esta es la manera de hacerlo. Si tienes algo más en mente, descríbelo. – jrockway

+1

Por un lado, ha establecido advertencias dos veces. O hazlo en la línea shebang ('#!/Usr/bin/perl -w') o hazlo con' use' - no necesitas hacer ambas cosas. Prefiero 'usar' porque tiene un alcance, pero algunos prefieren el' -w', quizás precisamente porque no tiene el alcance, o quizás porque es más corto. –

+0

@jrockway: Funciona bien para mí, pero me pregunto si puedo tener un código mejor que este. – Space

Respuesta

3

no veo cuál es tu problema, pero se puede usar un producto cartesiano genérico si está acostumbrado a SQL o algo:

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

Podría sugerir que su prototipo cartesiano sea prototipo, de modo que los usuarios no tengan que usar '\\' antes de todas sus listas. Además, lo ideal es que escupir los valores en el mismo orden en que se colocaron. –

+1

Además, por favor, 'use strict' y' warnings'. –

+0

¿Cómo lo diseñarías? –

1

foreach es preferible porque es legible. ¿A qué se refiere exactamente con "cada arreglo puede causar problemas" (¿qué problemas?) Y "los valores pueden no coincidir" (¿qué valores?)

+0

foreachs anidados son difíciles de leer y mantener. Use una solución de iterador como Algorithm :: Loops o Set :: CrossProduct. –

+0

En cuanto a su comentario, tendría que descargar los de CPAN. ¿Es mejor usar modismos incorporados como foreach o forzar al usuario a descargar paquetes externos? Prefiero las soluciones que tienen la menor cantidad posible de dependencias externas. –

+0

El código de mi módulo es mínimo. Él incluso podría levantarlo. No es preferible usar Foreach ya que codifica sus limitaciones. Otras soluciones son flexibles sin el trabajo de mono repetido que tienes que hacer cada vez que cambias el problema. –

-1

La única preocupación que podría surgir al utilizar bucles anidados es cierta ambigüedad en lo que $_ es . Considerando que ni siquiera lo está usando, no creo que haya una mejor manera de hacer lo que quiere.

Como nota al margen, me gustaría añadir que $_ está bien definido en este caso, pero como programador no me gustaría tratar con la sobrecarga de recordar a qué se refiere en cada paso.

¿Tiene alguna pregunta acerca de con el código?

+4

Uh, él no está usando $ _ en ninguna parte. – jrockway

+0

Por eso creo que no debería tener problemas aquí. –

+2

-1: "La única preocupación que podría tener aquí es el uso de fideos de huevo. Puede que no quiera lidiar con los gastos generales de recordar qué tienen que ver los fideos de huevo con su código. Sin embargo, como evita usarlo, debería hacerlo. no tengo ningún problema con este código ". –

-5

Usted puede utilizar un bucle clásico for lugar.

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

Pero eso aún deja los problemas y desajustes a los que se estaba refiriendo. Le sugiero que maneje estos problemas en la parte do_functions.

+3

Estoy seguro de que el código omitirá el último elemento de cada matriz. Es mejor utilizar el estilo 'foreach', por lo que no necesita perder el tiempo con los índices de matriz. –

+2

No entiendo por qué lo haría recomiendo una "solución" menos idiomática y más detallada que se parezca más a un aumento de velocidad mental para los mantenedores, y luego diga "pero esto no resuelve su problema". Si no resuelve su problema, o mejore su código en algunos Así que, ¿por qué estás publicando una respuesta? –

+1

Lo edité para que al menos sea algo correcto. No es que alguna vez use <= para bucles a partir de 0. $ i <@array es mucho más fácil de leer. – jrockway

2

Simplemente puede usar for.

(lo siento, no pudo resistir)

+3

Solía ​​ser un gran defensor de' foreach' - Pensé que usar 'for' debería ser para' 'b''''ops estilo C, para que sea más fácil de leer para mantenedores ("¿Es este un estilo C o un estilo Perl?"). Entonces me di cuenta de que, en Perl, casi nunca necesitas el ciclo 'for' de C-style, y ahora lo uso exclusivamente. –

+7

Creo que JB se estaba refiriendo a cómo "para" y "foreach" son sinónimos. Si al autor de la pregunta no le gusta foreach, puede elegir deletrearlo "para" en su lugar. –

+2

A juzgar por mi lectura del comentario de Chris Lutz, él entendió eso y estaba relatando que solía reservar "para" bucles C-style y "foreach" para iteración de lista, luego se dio cuenta de que nunca necesita bucles tipo C en Perl, así que ahora él felizmente usa "para" exclusivamente. (Y si esa no era su historia, de todos modos es mía). –

1

Si entiendo bien su pregunta, entonces preguntar cómo utilizar los hashes con foreach para evitar desajustes que tendría en su matriz de ejemplo ?.

Si es así, entonces aquí es un ejemplo:

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


También puede utilizar mientras & cada uno, que hace produce código más fácil en el ojo:

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

También tenga en cuenta Quité último bucle en el ejemplo anterior porque actualmente es superfluo con mis datos hash de ejemplo.

NB. Si planea enmendar claves o salir del bucle, lea en each y cómo afecta la iteración.

PS. ¡Este ejemplo no se trata del bucle, sino de que los datos se representan mejor como Hash y no como Array! (aunque no está claro al 100% de la pregunta, eso es así!).

6

Utilice mi módulo Set::CrossProduct, o use Algorithm::Loops.No debería tener que crear estructuras anidadas y codificadas para tratar estos problemas. Ambos módulos pueden hacerlo por usted para un número arbitrario de matrices.

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

No sólo eso, sino que el cursor le da maneras de desplazarse por el iterador por lo que no tiene por qué limitarse a la combinación actual. Puede inspeccionar las combinaciones anterior y siguiente, que pueden ser útiles para los límites (como cuando la siguiente tupla es un servidor diferente).

Tenga cuidado con las personas que desean crear todas las combinaciones en la memoria. No hay necesidad de hacer eso tampoco.

Cuestiones relacionadas