6

私はハッシュのPerlハッシュを持っています...約11または12要素の深さです。以下の構造を繰り返さないことを許してください!

一部のレベルには固定ラベル ( など) があり'NAMES''AGES'これらのレベルへのアクセスはラベルを直接使用できるので問題ありませんが、他の変数をループする必要があるため、ステートメントが非常に長くなります。これは、1 セットのループの半分の例です。

foreach my $person (sort keys %$people) {
        foreach my $name (sort keys %{$people->{$person}{'NAMES'}}) {
            foreach my $age (sort keys %{$people->{$person}{'NAMES'}{$name}{'AGES'}}) {
                . . . # and so on until I get to the push @list,$element; part

これは単なる例ですが、私が持っているものの構造に従います。固定名セクション (大文字の要素) を持たない方が短いかもしれませんが、他の場所で参照するために必要です。

各段階で要素をハッシュとしてキャストして短縮しようとしました。たとえば、2番目の foreach では、さまざまな形式を試しました。

foreach my $name (sort keys %{$person->{'NAMES'}})

しかし、これはうまくいきませんでした。以前に似たようなものを見たことがあるはずなので、セマンティクスが間違っている可能性があります。

ハッシュのハッシュに関するページと、ハッシュとその要素への参照などを調べましたが、うまくいきませんでした。ループの例を見たことがありwhile eachますが、それらは特に短くも簡単にも実装できるようには見えません。たぶん、これを行うには別の方法があり、要点がわかりません。ループの完全なセットを 1 回書き出しましたが、foreachあと 6 回ほど繰り返す必要がなければ素晴らしいと思います。

もちろん、「簡単な」方法はないかもしれませんが、すべての助けに感謝します!

4

4 に答える 4

6

$person値を何かに割り当てる必要がある内側のループを短縮するために、キーです。

foreach my $person_key (sort keys %$people) {
    my $person = $people->{$person_key};
    my $names  = $person->{NAMES};
    foreach my $name (sort keys %$names) {
于 2012-11-15T18:12:34.610 に答える
5

また、各キーワードで作業することもできます。これは間違いなく役立つはずです。

while( my ($person, $val1) = each(%$people) ) {
    while( my ($name, $val2) = each(%$val1) ) {
        while( my ($age, $val3) = each(%$val2) ) {
            print $val3->{Somekey};
于 2012-11-15T18:37:19.670 に答える
4

データ構造の File::Find の一種であるData::Walkを使用できます。

于 2012-11-15T22:46:56.837 に答える
4

もう少し柔軟なソリューションを構築したい場合は、データ ツリーを再帰的にトラバースできます。次のデータ ツリーの例を考えてみましょう (任意の深さ)。

サンプルデータ

my %people = (
    memowe => {
        NAMES => {
            memo        => {AGE => 666},
            we          => {AGE => 667},
        },
    },
    bladepanthera => {
        NAMES => {
            blade       => {AGE => 42},
            panthera    => {AGE => 17},
        },
    },
);

AGEあなたの質問から、葉(この場合は s )で作業したいだけだと結論付けました。traverseしたがって、キーでソートされた深さ優先の順序で見つかる可能性のあるすべてのリーフで、特定の subref を実行する再帰サブルーチンを作成できます。この subref は、便宜上、leave 自体とハッシュ キーのパスを取得します。

準備

sub traverse (&$@) {
    my ($do_it, $data, @path) = @_;

    # iterate
    foreach my $key (sort keys %$data) {

        # handle sub-tree
        if (ref($data->{$key}) eq 'HASH') {
            traverse($do_it, $data->{$key}, @path, $key);
            next;
        }

        # handle leave
        $do_it->($data->{$key}, @path, $key);
    }
}

インラインコメントから、この男がどのように機能するかはかなり明確だと思います。必要に応じて、リーフのみではなく、すべてのノードで coderef を実行しても大きな変更はありません。よく知られているor構文で使用するのは非常に簡単なので、便宜上、例外的にここにプロトタイプを追加したことに注意してください。traversemapgrep

データに対して何かを実行する

traverse { say shift . " (@_)" } \%people;

@pathまた、ハッシュ参照で機能し、暗黙的な空のリストで初期化したことにも注意してください。

出力:

42 (bladepanthera NAMES blade AGE)
17 (bladepanthera NAMES panthera AGE)
666 (memowe NAMES memo AGE)
667 (memowe NAMES we AGE)

指定されたサブルーチン ( として記述{ block }) は、指定されたデータに対して何でも実行できます。たとえば、このより読みやすい push サブルーチン:

my @flattened_people = ();

traverse {
    my ($thing, @path) = @_;
    push @flattened_people, { age => $thing, path => \@path };
} \%people;
于 2012-11-15T18:52:38.897 に答える