如何在不在Perl中嵌套foreach语句的情况下处理嵌套循环?

时间:2009-08-26 06:18:55

标签: perl nested-loops

我有一个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 个答案:

答案 0 :(得分:6)

使用我的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";
    }

不仅如此,光标还为您提供了在迭代器中移动的方法,因此您不必将自己限制在当前组合中。您可以检查上一个和下一个组合,这可能对边界有用(例如下一个元组是不同服务器的位置)。

注意那些想要在内存中创建所有组合的人。也没有必要这样做。

答案 1 :(得分:3)

我看不出你的问题是什么,但如果你习惯使用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 :(得分:2)

您只需使用for

(抱歉,无法抗拒)

答案 3 :(得分:1)

foreach更可取,因为它是可读的。究竟是什么意思“每个阵列都会导致问题”(什么问题?)和“价值可能不匹配”(什么价值?)

答案 4 :(得分:1)

如果我正确理解了你的问题,那么你问如何使用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。如果您打算修改密钥或突破循环,请阅读each以及它如何影响迭代。

PS。这个例子不是关于循环,而是关于数据最好表示为哈希而不是数组! (虽然不清楚100%的问题是这样的!)。

答案 5 :(得分:-1)

使用嵌套循环时我可能遇到的唯一问题是$_的含义有些模糊。考虑到你甚至没有使用它,我认为没有更好的方法来做你想要的。

作为旁注,我想补充说$_在这种情况下得到了很好的定义,但作为程序员,我可能不想处理在每一步中记住它所指的内容的开销。 / p>

您对代码有任何特定的问题吗?

答案 6 :(得分:-5)

您可以使用经典的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部分处理这些问题。