NAME

perldsc - Perl のデータ構造クックブック

DESCRIPTION

Perl のリリース 5.0 は、私たちに複雑なデータ構造をもたらします。 あなたは三次元の配列のようなものだって記述できるんです!

    for my $x (1 .. 10) {
        for my $y (1 .. 10) {
            for my $z (1 .. 10) {
                $AoA[$x][$y][$z] =
                    $x ** $y + $z;
            }
        }
    }

ああ、これは単純に見えるけど見えないところに複雑なものが隠れてるんです!

これをどのように出力しますか? なぜ単に print @AoA としては いけないのでしょう? ソートはどうやるのですか? どうすれば関数に引数として渡したり、関数の 戻り値として受け取ることができるでしょうか? それはオブジェクトですか? 後で読み返すためにディスクにセーブすることが できますか? 配列の行全体や列全体にどうアクセスしますか? すべての値は数値でなければいけないのでしょうか?

見ての通り、これは簡単に混乱してしまいます。 これに対する portion of blame の一部はリファレンスベースの実装に 帰することができるとしても、初心者向けにデザインされた例を持った ドキュメントが欠けているということが大きいでしょう。

本ドキュメントは詳しく説明することが目的ですが、あなたが開発しようと 考えるかもしれないデータ構造の多くの相違点の扱いは理解可能なものです。 詳細な説明はサンプルのレシピ集に取っておくべきものです。 あなたがこれら複雑なデータ構造の一つを作成する必要がある場合、あなたは ここにある例からひょいと持っていくことができます。

これら可能な構造をそれぞれ詳しく見て行きましょう。 以下の様に、それぞれセクションとして独立しています。

しかし今のところは、これら全てのデータ構造全てに共通の一般的な問題を 見ていきましょう。

リファレンス

多次元配列も含めた Perl におけるすべてのデータ構造を理解するに当たって 最も重要な事は、Perl の @ARRAY%HASH は外見はそうは見えなくても、 内部的には全て一次元であるということです。 これらのものはスカラー値(文字列や数値、リファレンス)だけを保持することが できます。 (配列やハッシュは)直接他の配列やハッシュを保持することはできませんが、 他の配列、ハッシュに対する リファレンス を保持することができます。

配列へのリファレンスやハッシュのリファレンスを、本当の配列やハッシュと 同じやり方で使うことはできません。 C や C++ プログラマーは、ある配列と(配列要素の型への)ポインターとの間の 区別していませんから混乱してしまうかもしれません。 もし本当に混乱してしまったら、構造体と構造体へのポインタの違いのような ものであると考えてください。

perlref を読めばリファレンスに関してより多くのことがありますし、 そうすべきです。 一言で言えば、リファレンスは自分が何を指しているかを知っているポインターの ようなものです。 (オブジェクトもまたリファレンスのようなものですが、いますぐそうする必要は ないでしょう。) これはつまり、あなたが二次元、あるいはそれ以上の次元を持った配列やハッシュに アクセスしようとしたとき、実際にはそれが次のレベルへのリファレンスを 保持している一次元の実体(entity)であるということなのです。 あなたはこれを二次元配列であるかのように使うことができます。 これはほとんどすべての C の多次元配列が動作しているのと同じ方法です。

    $array[7][12]                       # 配列の配列
    $array[7]{string}                   # ハッシュの配列
    $hash{string}[7]                    # 配列のハッシュ
    $hash{string}{'another string'}     # ハッシュのハッシュ

トップレベルはリファレンスのみで構成されるので、これらの配列を単純に print() 関数を使って出力しようとすれば、次のような良くわから ない結果となるでしょう。

    my @AoA = ( [2, 3], [4, 5, 7], [0] );
    print $AoA[1][2];
  7
    print @AoA;
  ARRAY(0x83c38)ARRAY(0x8b194)ARRAY(0x8b1d0)

これはPerlがあなたの使う変数のデリファレンス(dereference)をこっそりやらないからです。 リファレンスが参照しているものを取り出したいのなら、${$blah}, @{$blah}, @{$blah[$i]} のような型指定子を前置したり、あるいは$a->[3], $h->{fred}, $ob->method()->[3] のようにpointer arrowを 後置して自分自身でデリファレンスをしなければなりません。

よくある間違い

配列の配列を構築するようなとき犯しやすい間違いとして、誤って要素の 数を数えてしまうことと、同じメモリ位置にあるものリファレンスを 繰り返しとってしまうという二つがあります。 以下の例は、ネストした配列の代わりにその数を数えてしまうという例です。

    for my $i (1..10) {
        my @array = somefunc($i);
        $AoA[$i] = @array;      # WRONG!
    }

これは配列をスカラーに代入し、その要素数を取得するという単純な サンプルです。 もし本当にそれがあなたのやりたいことであるというのであれば、 それを明確にするために以下の様にすることが良いかもしれません:

    for my $i (1..10) {
        my @array = somefunc($i);
        $counts[$i] = scalar @array;
    }

次の例は、同じメモリ位置にあるリファレンスを何度もくり返し取って しまうというものです。

    # Either without strict or having an outer-scope my @array;
    # declaration.

    for my $i (1..10) {
        @array = somefunc($i);
        $AoA[$i] = \@array;     # WRONG!
    }

さて、何が問題なんでしょう? 正しいように見えますが、違うのでしょうか? リファレンスの配列が必要だといいましたよね; あれ、あなたもう 作ってるじゃないですか!

残念なことに、これは正しいのですがまだおかしいのです。 @AoA にあるすべてのリファレンスは全く同じ場所を参照していて、 そのためそういったリファレンスはすべて最後に @array にあったものを 保持してるんです! つまり、以下の C プログラムにある問題と同じです。

    #include <pwd.h>
    main() {
        struct passwd *getpwnam(), *rp, *dp;
        rp = getpwnam("root");
        dp = getpwnam("daemon");

        printf("daemon name is %s\nroot name is %s\n",
                dp->pw_name, rp->pw_name);
    }

これの出力はこうなります:

    daemon name is daemon
    root name is daemon

この問題は、rpdp の両方が同じメモリ位置を指している ポインターであるということです! C では、新たなメモリを確保するために自分でmalloc()することを 忘れてはいけません。 Perlでは、代わりに配列コンストラクタ [] や ハッシュコンストラクタ {} を使います。 以下の例は、先に挙げた間違ったコード片の正しいやり方です:

    # Either without strict or having an outer-scope my @array;
    # declaration.

    for my $i (1..10) {
        @array = somefunc($i);
        $AoA[$i] = [ @array ];
    }

ブラケットは新たな配列への参照を作り出し、代入のときに @array の内容を コピー します。 これがあなたの望むことです。

以下のようなやり方でも同様の結果となります:

    # Either without strict or having an outer-scope my @array;
    # declaration.
    for my $i (1..10) {
        @array = 0 .. $i;
        $AoA[$i]->@* = @array;
    }

同じことなんでしょうか? そう、同じでもあるでしょうし、 違うとも言えるでしょう。 そこにある微妙な違いとは、大かっこの中にある何かを代入しようとしたときは、 それは常にデータの新たな コピー による 新たなリファレンスであるということです。 そうでない場合には、代入の左辺で $AoA[$i]->@* のデリファレンスを しようとするかもしれません。 これは $AoA[$i] が未定義の状態であるかあるいはすでにリファレンスが 入っているかということに依存しています。 すでに次のようにして @AoA をリファレンスのために使っていた場合:

    $AoA[3] = \@another_array;

そして左辺の間接代入では、既に存在するリファレンスを 使うことになります:

    $AoA[3]->@* = @array;

もちろんこれは、@another_array を壊すという「興味深い」効果を もたらすでしょう。 (プログラマーが何かを「興味深い」といったときに、 それは「好奇心をそそるもの」というよりはむしろ「困ったもの」、 「困難なもの」という意味で使われるということに 気がついたことがありますか? :-)

[]{}による配列やハッシュのコンストラクターを常に使うのだと言うことを 忘れないでください; そうすれば、たとえそれが効率的に最良でない場合が あるにせよ、あなたは気持ちよくいられるでしょう。

驚くべきことに、以下の例は危険なもののように見えるにも関らず、 実際にはきちんと動作します。

    for my $i (1..10) {
        my @array = somefunc($i);
        $AoA[$i] = \@array;
    }

なぜなら、my() はコンパイル時に処理される宣言文ではなく、 実行時に処理される文であるからです。 これはつまり、my で宣言された変数は、ループを通過する度に新たに 再生成されるということです。 このため、このコードが毎回同じ変数のリファレンスを格納しているように 見えるにも関らず、実際にはそうではないのです! これは、熟練したプログラマー以外の人を誤解させる危険性をはらんだ上で、 より効率の良いコードを作ることのできるような微妙な違いです。 ですから、私は普段は初心者に対してこれを使わないように教えるのです。 事実、関数に対してパラメーターを渡すときを除いて、私はプログラム中に 参照演算子がうじゃうじゃでて来ることを好みません。 その代わり、私は初心者に(初心者と、我々の大部分は)レキシカル (または動的)スコープや隠れた参照カウントに影響されるよりは、 より理解しやすい [], {} をもっと使うようにすべきであると アドバイスしています。

デリファレンスを書くもう一つの方法があることにも注意してください! これらの二つの行は等価です:

    $AoA[$i]->@* = @array;
    @{ $AoA[$i] } = @array;

接尾デリファレンス と呼ばれる最初の形式は、一般的により読みやすいです; なぜならこの式は左から右に読むことができ、囲まれている中かっこがないからです。 一方、これはより新しくもあります。 これは 2014 年に言語に追加されたので、古いコードでは、もう一つの形式である 両面接辞デリファレンス (circumfix dereference) に しばしば遭遇するでしょう。

まとめるとこうなります:

    $AoA[$i] = [ @array ];     # 普通はこれが最善
    $AoA[$i] = \@array;        # 危険; just how my() was that array?
    $AoA[$i]->@*  = @array;    # ほとんどのプログラマには技巧的過ぎ
    @{ $AoA[$i] } = @array;    # 同様に技巧的で、読みにくい

優先順位に関する警告

@{$AoA[$i]} のときと同様、以下の二つは同じ動作をします。

    $aref->[2][2]       # 明快
    $$aref[2][2]        # まぎらわしい

これは、Perlの優先順位規則では 5つの前置形式のデリファレンス演算子 ($ @ * % &)は、後置形式の添え字付け演算子のブラケットや カーリーブレースよりも強く結び付くからなのです! これは *a[i] を、ai番目が指しているものであるとみなすことに 慣れきった C/C++ プログラマーにとっては大きな衝撃であることは 疑いないでしょう。 つまり、まず最初に添え字を取って、それから添え字付けされたものの デリファレンスを行うということです。 それは C では正しいことですが、これは C じゃないのです。

Perlにおける等価な構造の $$aref[$i] では、最初に $aref の デリファレンスをして、$aref から配列への参照にします; そしてその デリファレンスをして、最後に $AoA によって指されている配列の i番目 の値を 取り出します。 もし C の記法を望むのなら、左から数えて i 番目の要素を明示的に デリファレンスするために、$AoA[$i]->$* と記述できます。

なぜ常に use strict を使うべきなのか

もしこのことが、価値あるものというより恐ろしいものに感じられても リラックスしてください。 Perlはありがちな落とし穴を避けるための幾つかの機能を備えています。 混乱を避けるための最善の方法は、すべてのプログラムを以下の 行で始めることです:

    use strict;

この場合、あなたはすべての変数を my() を使って宣言することが強制され、 間違った「シンボリックデリファレンス」が禁止されます。 したがって、以下のようにした場合:

    my $aref = [
        [ "fred", "barney", "pebbles", "bambam", "dino", ],
        [ "homer", "bart", "marge", "maggie", ],
        [ "george", "jane", "elroy", "judy", ],
    ];

    print $aref[2][2];

宣言されていない変数 @aref に間違ってアクセスしたために、 コンパイラは コンパイル時 にフラグをエラーに設定し、次のように 書くということあなたに思い出させるでしょう。

    print $aref->[2][2]

デバッグ

複雑なデータ構造をダンプするために、デバッガの x コマンドが使えます。 たとえば、先の例にあった $AoA に対する代入をデバッガに渡したとき、 その出力はこうなります。

    DB<1> x $AoA
    $AoA = ARRAY(0x13b5a0)
       0  ARRAY(0x1f0a24)
          0  'fred'
          1  'barney'
          2  'pebbles'
          3  'bambam'
          4  'dino'
       1  ARRAY(0x13b558)
          0  'homer'
          1  'bart'
          2  'marge'
          3  'maggie'
       2  ARRAY(0x13b540)
          0  'george'
          1  'jane'
          2  'elroy'
          3  'judy'

コード例

ここにあるちょっとしたコメントは、様々な形式のデータ構造へのアクセスを 例示するサンプルです。

配列の配列

配列の配列の宣言

 @AoA = (
        [ "fred", "barney" ],
        [ "george", "jane", "elroy" ],
        [ "homer", "marge", "bart" ],
      );

配列の配列の生成

 # reading from file
 while ( <> ) {
     push @AoA, [ split ];
 }

 # calling a function
 for $i ( 1 .. 10 ) {
     $AoA[$i] = [ somefunc($i) ];
 }

 # using temp vars
 for $i ( 1 .. 10 ) {
     @tmp = somefunc($i);
     $AoA[$i] = [ @tmp ];
 }

 # add to an existing row
 push $AoA[0]->@*, "wilma", "betty";

配列の配列へのアクセスと出力

 # one element
 $AoA[0][0] = "Fred";

 # another element
 $AoA[1][1] =~ s/(\w)/\u$1/;

 # print the whole thing with refs
 for $aref ( @AoA ) {
     print "\t [ @$aref ],\n";
 }

 # print the whole thing with indices
 for $i ( 0 .. $#AoA ) {
     print "\t [ $AoA[$i]->@* ],\n";
 }

 # print the whole thing one at a time
 for $i ( 0 .. $#AoA ) {
     for $j ( 0 .. $AoA[$i]->$#* ) {
         print "elem at ($i, $j) is $AoA[$i][$j]\n";
     }
 }

配列のハッシュ

配列のハッシュの宣言

 %HoA = (
        flintstones        => [ "fred", "barney" ],
        jetsons            => [ "george", "jane", "elroy" ],
        simpsons           => [ "homer", "marge", "bart" ],
      );

配列のハッシュの生成

 # reading from file
 # flintstones: fred barney wilma dino
 while ( <> ) {
     next unless s/^(.*?):\s*//;
     $HoA{$1} = [ split ];
 }

 # reading from file; more temps
 # flintstones: fred barney wilma dino
 while ( $line = <> ) {
     ($who, $rest) = split /:\s*/, $line, 2;
     @fields = split ' ', $rest;
     $HoA{$who} = [ @fields ];
 }

 # calling a function that returns a list
 for $group ( "simpsons", "jetsons", "flintstones" ) {
     $HoA{$group} = [ get_family($group) ];
 }

 # likewise, but using temps
 for $group ( "simpsons", "jetsons", "flintstones" ) {
     @members = get_family($group);
     $HoA{$group} = [ @members ];
 }

 # append new members to an existing family
 push $HoA{flintstones}->@*, "wilma", "betty";

配列のハッシュへのアクセスと出力

 # one element
 $HoA{flintstones}[0] = "Fred";

 # another element
 $HoA{simpsons}[1] =~ s/(\w)/\u$1/;

 # print the whole thing
 foreach $family ( keys %HoA ) {
     print "$family: $HoA{$family}->@* \n"
 }

 # print the whole thing with indices
 foreach $family ( keys %HoA ) {
     print "family: ";
     foreach $i ( 0 .. $HoA{$family}->$#* ) {
         print " $i = $HoA{$family}[$i]";
     }
     print "\n";
 }

 # print the whole thing sorted by number of members
 foreach $family ( sort { $HoA{$b}->@* <=> $HoA{$a}->@* } keys %HoA ) {
     print "$family: $HoA{$family}->@* \n"
 }

 # print the whole thing sorted by number of members and name
 foreach $family ( sort {
                            $HoA{$b}->@* <=> $HoA{$a}->@*
                                          ||
                                      $a cmp $b
            } keys %HoA )
 {
     print "$family: ", join(", ", sort $HoA{$family}->@* ), "\n";
 }

ハッシュの配列

ハッシュの配列の宣言

 @AoH = (
        {
            Lead     => "fred",
            Friend   => "barney",
        },
        {
            Lead     => "george",
            Wife     => "jane",
            Son      => "elroy",
        },
        {
            Lead     => "homer",
            Wife     => "marge",
            Son      => "bart",
        }
  );

ハッシュの配列の生成

 # reading from file
 # format: LEAD=fred FRIEND=barney
 while ( <> ) {
     $rec = {};
     for $field ( split ) {
         ($key, $value) = split /=/, $field;
         $rec->{$key} = $value;
     }
     push @AoH, $rec;
 }

 # reading from file
 # format: LEAD=fred FRIEND=barney
 # no temp
 while ( <> ) {
     push @AoH, { split /[\s+=]/ };
 }

 # calling a function  that returns a key/value pair list, like
 # "lead","fred","daughter","pebbles"
 while ( %fields = getnextpairset() ) {
     push @AoH, { %fields };
 }

 # likewise, but using no temp vars
 while (<>) {
     push @AoH, { parsepairs($_) };
 }

 # add key/value to an element
 $AoH[0]{pet} = "dino";
 $AoH[2]{pet} = "santa's little helper";

ハッシュの配列へのアクセスと出力

 # one element
 $AoH[0]{lead} = "fred";

 # another element
 $AoH[1]{lead} =~ s/(\w)/\u$1/;

 # print the whole thing with refs
 for $href ( @AoH ) {
     print "{ ";
     for $role ( keys %$href ) {
         print "$role=$href->{$role} ";
     }
     print "}\n";
 }

 # print the whole thing with indices
 for $i ( 0 .. $#AoH ) {
     print "$i is { ";
     for $role ( keys $AoH[$i]->%* ) {
         print "$role=$AoH[$i]{$role} ";
     }
     print "}\n";
 }

 # print the whole thing one at a time
 for $i ( 0 .. $#AoH ) {
     for $role ( keys $AoH[$i]->%* ) {
         print "elem at ($i, $role) is $AoH[$i]{$role}\n";
     }
 }

ハッシュのハッシュ

ハッシュのハッシュの宣言

 %HoH = (
        flintstones => {
                lead      => "fred",
                pal       => "barney",
        },
        jetsons     => {
                lead      => "george",
                wife      => "jane",
                "his boy" => "elroy",
        },
        simpsons    => {
                lead      => "homer",
                wife      => "marge",
                kid       => "bart",
        },
 );

ハッシュのハッシュの生成

 # reading from file
 # flintstones: lead=fred pal=barney wife=wilma pet=dino
 while ( <> ) {
     next unless s/^(.*?):\s*//;
     $who = $1;
     for $field ( split ) {
         ($key, $value) = split /=/, $field;
         $HoH{$who}{$key} = $value;
     }

 # reading from file; more temps
 while ( <> ) {
     next unless s/^(.*?):\s*//;
     $who = $1;
     $rec = {};
     $HoH{$who} = $rec;
     for $field ( split ) {
         ($key, $value) = split /=/, $field;
         $rec->{$key} = $value;
     }
 }

 # calling a function  that returns a key,value hash
 for $group ( "simpsons", "jetsons", "flintstones" ) {
     $HoH{$group} = { get_family($group) };
 }

 # likewise, but using temps
 for $group ( "simpsons", "jetsons", "flintstones" ) {
     %members = get_family($group);
     $HoH{$group} = { %members };
 }

 # append new members to an existing family
 %new_folks = (
     wife => "wilma",
     pet  => "dino",
 );

 for $what (keys %new_folks) {
     $HoH{flintstones}{$what} = $new_folks{$what};
 }

ハッシュのハッシュに対するアクセスと出力

 # one element
 $HoH{flintstones}{wife} = "wilma";

 # another element
 $HoH{simpsons}{lead} =~ s/(\w)/\u$1/;

 # print the whole thing
 foreach $family ( keys %HoH ) {
     print "$family: { ";
     for $role ( keys $HoH{$family}->%* ) {
         print "$role=$HoH{$family}{$role} ";
     }
     print "}\n";
 }

 # print the whole thing  somewhat sorted
 foreach $family ( sort keys %HoH ) {
     print "$family: { ";
     for $role ( sort keys $HoH{$family}->%* ) {
         print "$role=$HoH{$family}{$role} ";
     }
     print "}\n";
 }

 # print the whole thing sorted by number of members
 foreach $family ( sort { $HoH{$b}->%* <=> $HoH{$a}->%* } keys %HoH ) {
     print "$family: { ";
     for $role ( sort keys $HoH{$family}->%* ) {
         print "$role=$HoH{$family}{$role} ";
     }
     print "}\n";
 }

 # establish a sort order (rank) for each role
 $i = 0;
 for ( qw(lead wife son daughter pal pet) ) { $rank{$_} = ++$i }

 # now print the whole thing sorted by number of members
 foreach $family ( sort { $HoH{$b}->%* <=> $HoH{$a}->%* } keys %HoH ) {
     print "$family: { ";
     # and print these according to rank order
     for $role ( sort { $rank{$a} <=> $rank{$b} }
                                               keys $HoH{$family}->%* )
     {
         print "$role=$HoH{$family}{$role} ";
     }
     print "}\n";
 }

もっと複雑なレコード

もっと複雑なレコードの宣言

以下に示すのは、多くの異なった種類のフィールドを持ったレコードの 作成と使用のサンプルです。

     $rec = {
         TEXT      => $string,
         SEQUENCE  => [ @old_values ],
         LOOKUP    => { %some_table },
         THATCODE  => \&some_function,
         THISCODE  => sub { $_[0] ** $_[1] },
         HANDLE    => \*STDOUT,
     };

     print $rec->{TEXT};

     print $rec->{SEQUENCE}[0];
     $last = pop $rec->{SEQUENCE}->@*;

     print $rec->{LOOKUP}{"key"};
     ($first_k, $first_v) = each $rec->{LOOKUP}->%*;

     $answer = $rec->{THATCODE}->($arg);
     $answer = $rec->{THISCODE}->($arg1, $arg2);

     # careful of extra block braces on fh ref
     print { $rec->{HANDLE} } "a string\n";

     use FileHandle;
     $rec->{HANDLE}->autoflush(1);
     $rec->{HANDLE}->print(" a string\n");

複雑なレコードのハッシュの宣言

     %TV = (
        flintstones => {
            series   => "flintstones",
            nights   => [ qw(monday thursday friday) ],
            members  => [
                { name => "fred",    role => "lead", age  => 36, },
                { name => "wilma",   role => "wife", age  => 31, },
                { name => "pebbles", role => "kid",  age  =>  4, },
            ],
        },

        jetsons     => {
            series   => "jetsons",
            nights   => [ qw(wednesday saturday) ],
            members  => [
                { name => "george",  role => "lead", age  => 41, },
                { name => "jane",    role => "wife", age  => 39, },
                { name => "elroy",   role => "kid",  age  =>  9, },
            ],
         },

        simpsons    => {
            series   => "simpsons",
            nights   => [ qw(monday) ],
            members  => [
                { name => "homer", role => "lead", age  => 34, },
                { name => "marge", role => "wife", age => 37, },
                { name => "bart",  role => "kid",  age  =>  11, },
            ],
         },
      );

複雑なレコードのハッシュの生成

     # ファイルからの読み込み
     # これはファイルそれ自身が、先の例で示したように raw data
     # format になっているのでとても簡単です。perlは、データのよ
     # うに宣言されているのであれば、複雑なデータ構造を喜んで解
     # 析します; ですからとても簡単に済むことがあるのです

     # フィールド毎に構築する
     $rec = {};
     $rec->{series} = "flintstones";
     $rec->{nights} = [ find_days() ];

     @members = ();
     # このファイルは フィールド=値 という構文となっていると仮定
     while (<>) {
         %fields = split /[\s=]+/;
         push @members, { %fields };
     }
     $rec->{members} = [ @members ];

     # now remember the whole thing
     $TV{ $rec->{series} } = $rec;

     ###########################################################
     # ここで、あなたは同じデータへの戻るポインターのような興味
     # 深い追加フィールドを作成したいと思うかもしれません; これ
     # により、ある一つを変更するとすべてが変更されます; この例
     # で言えば、{kid}というフィールドは子供のレコードの配列で
     # すが、これをレコードの重複をなくして更新の問題もなくすこ
     # とができるようなものです。
     ###########################################################
     foreach $family (keys %TV) {
         $rec = $TV{$family}; # temp pointer
         @kids = ();
         for $person ( $rec->{members}->@* ) {
             if ($person->{role} =~ /kid|son|daughter/) {
                 push @kids, $person;
             }
         }
         # REMEMBER: $rec and $TV{$family} point to same data!!
         $rec->{kids} = [ @kids ];
     }

     # あなたは配列をコピーしましたが、配列それ自身はコピー
     # されていないオブジェクトへのポインターから構成されています。
     # これはあなたがbartを作ったら古いものを通してで取得され
     # るということです

     $TV{simpsons}{kids}[0]{age}++;

     # そしてこれは変更されたものです
     print $TV{simpsons}{members}[2]{age};

     # なぜなら $TV{simpsons}{kids}[0] と $TV{simpsons}{members}[2]
     # は両方とも同じ無名のハッシュテーブルを指しているからです

     # 全体を表示する
     foreach $family ( keys %TV ) {
         print "the $family";
         print " is on during $TV{$family}{nights}->@*\n";
         print "its members are:\n";
         for $who ( $TV{$family}{members}->@* ) {
             print " $who->{name} ($who->{role}), age $who->{age}\n";
         }
         print "it turns out that $TV{$family}{lead} has ";
         print scalar ( $TV{$family}{kids}->@* ), " kids named ";
         print join (", ", map { $_->{name} } $TV{$family}{kids}->@* );
         print "\n";
     }

データベースの tie

(ハッシュのハッシュのような)複数レベルのデータ構造を dbm ファイルに tie することは簡単にはできません。 問題は、GDBM と Berkeley DB はサイズに制限があり、それを超えることが できないということで、また、ディスク上にあるものを参照する方法についても 問題があります。 部分的にこれを解決しようとしている実験的なモジュールの一つに、 MLDBM というものがあります。 ソースコードは perlmodlib にあるように、 あなたのお近くの CPAN サイトを確かめてください。

SEE ALSO

perlref, perllol, perldata, perlobj

AUTHOR

Tom Christiansen <tchrist@perl.com>