5

以下に示すように、foreach ループをネストする 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は長い時間がかかり、読みにくく、あまりきれいではありません。ハッシュまたは他の巧妙な構造を使用してこの構造をコーディングするより良い方法を提案できる人はいますか?

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 でハッシュを使用する方法を尋ねていますか?.

もしそうなら、ここに一例があります:

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

foreach読みやすいのでおすすめです。「各配列が問題を引き起こす可能性がある」(どのような問題が発生する可能性があるか) および「値が一致しない可能性がある」(どのような値が発生する可能性があるか) とは、正確にはどういう意味ですか

于 2009-08-26T06:29:42.020 に答える
-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 に答える