Как я могу обрабатывать вложенные циклы без вложенных операторов foreach в Perl? - PullRequest
5 голосов
/ 26 августа 2009

У меня есть Perl-скрипт, который вставляет циклы foreach, как показано ниже. Это занимает много времени:

#! /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 таких утверждений занимает много времени, их трудно читать и они не очень красивые. Кто-нибудь может предложить лучший способ кодирования этой структуры с использованием хешей или какой-то другой умной структуры?

Ответы [ 7 ]

6 голосов
/ 27 августа 2009

Используйте мой Set :: CrossProduct модуль или используйте Algorithm :: Loops . Вам не нужно создавать жестко закодированные вложенные структуры для решения этих проблем. Оба эти модуля могут сделать это для вас для произвольного числа массивов.

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

Не только это, но и курсор дает вам возможность перемещаться в итераторе, поэтому вам не нужно ограничивать себя текущей комбинацией. Вы можете проверить предыдущую и следующую комбинации, которые могут быть полезны для границ (например, если следующий кортеж - это другой сервер).

Остерегайтесь людей, которые хотят создать все комбинации в памяти. Нет необходимости это делать.

3 голосов
/ 26 августа 2009

Я не понимаю, в чем ваша проблема, но вы можете использовать универсальный декартовый продукт, если вы привыкли к SQL или что-то еще:

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";
}
2 голосов
/ 26 августа 2009

Вы можете просто использовать for.

(извините, не смог устоять)

1 голос
/ 26 августа 2009

Если я правильно понимаю ваш вопрос, то вы спрашиваете, как использовать хэши с foreach, чтобы избежать несоответствий, которые вы имели бы в своем примере массива ?.

Если это так, то вот один пример:

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


Вы также можете использовать while &, каждый из которых выдает более простой код:

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

Также обратите внимание, что я удалил последний цикл в вышеприведенном примере, потому что в настоящее время он лишний с моими данными хэша примера.

NB. Если вы планируете изменить ключи или выйти из цикла, прочтите, пожалуйста, каждый и как это влияет на итерацию.

PS. Этот пример не о цикле, а о том, что данные лучше всего представлять в виде хэша, а не массива! (хотя это не ясно, 100% из вопроса, который так!).

1 голос
/ 26 августа 2009

foreach предпочтительнее, потому что он читабелен. Что именно вы подразумеваете под «каждый массив может вызвать проблемы» (какие проблемы?) И «значения могут не совпадать» (какие значения?)

0 голосов
/ 26 августа 2009

Вместо этого вы можете использовать классический цикл for.

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

Но это все еще оставляет проблемы и несоответствия, к которым вы относились. Я предлагаю вам решить эти проблемы в do_functions части.

0 голосов
/ 26 августа 2009

Единственная проблема, которая может возникнуть при использовании вложенных циклов, - это некоторая двусмысленность в том, что такое $_. Учитывая, что вы даже не используете его, я не думаю, что есть лучший способ сделать то, что вы хотите.

В качестве обозначения я хотел бы добавить, что $_ в этом случае хорошо определено, но как программист я не хочу иметь дело с дополнительными затратами на запоминание того, к чему он относится на каждом этапе.

Есть ли у вас какие-либо специфические проблемы с кодом?

...