5

我有一个 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语句需要很长时间,而且很难阅读而且不是很漂亮。谁能建议一种更好的方法来使用散列或其他一些聪明的结构来编码这个结构?

4

7 回答 7

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

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

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

于 2009-08-27T17:40:09.300 回答
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";
}
于 2009-08-26T06:38:45.480 回答
2

你可以简单地使用for.

(对不起,无法抗拒)

于 2009-08-26T06:57:58.750 回答
1

foreach更可取,因为它是可读的。“每个数组都可能导致问题”(什么问题?)和“值可能不匹配”(什么值?)

于 2009-08-26T06:29:42.020 回答
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 & each 确实会在眼睛上产生更简单的代码:

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

另请注意,我在上面的示例中删除了最后一个循环,因为它目前对我的示例哈希数据是多余的。

注意。如果您打算修改密钥或跳出循环,请阅读每个密钥以及它如何影响迭代。

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

于 2009-08-26T08:04:37.680 回答
-1

使用嵌套循环时我可能唯一关心的问题是存在的一些歧义$_。考虑到你甚至没有使用它,我认为没有更好的方法来做你想做的事。

作为旁注,我想补充一下$_在这种情况下定义良好,但作为一名程序员,我可能不想处理在每个步骤中记住它所指的内容的开销。

您对代码有任何具体问题吗?

于 2009-08-26T06:38:22.110 回答
-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

于 2009-08-26T06:40:14.730 回答