NAME

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


DESCRIPTION

Perl というプログラミング言語の、5.0 以前のもので最も欠けていた一つの機能とは 複雑なデータ構造でした。 言語による直接のサポートがなくても、一部の英雄的なプログラマー達は複雑な データ構造をエミュレートしていました。 しかし、それはとても大変な作業でした。 あなたは(多次元配列をエミュレートするために) awk から借りてきた $m{$AoA,$b} という記法を使うことができましたが、これは実際には そのキーを "$AoA$b" のような連結された文字列にしているので、 分解やソートが困難でした。 もっと命知らずのプログラマーたちはデータ構造を優しく put するために、Perl の 内部シンボルテーブルを直接ハックすることさえ行って、開発や保守を困難にする 戦略をとったのです。

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

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

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

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

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

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

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

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


リファレンス

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

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

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

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

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

    @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 $i (1..10) {
        @array = somefunc($i);
        $AoA[$i] = @array;      # WRONG!
    }

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

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

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

    for $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では、代わりに配列コンストラクタ [] や ハッシュコンストラクタ {} を使います。 以下の例は、先に挙げた間違ったコード片の正しいやり方です:

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

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

以下のようなやり方でも同様の結果となりますが、読みやすさという点では 劣るということに注意してください。

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

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

    $AoA[3] = \@another_array;

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

    @{$AoA[3]} = @array;

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

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

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

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

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

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

    $AoA[$i] = [ @array ];      # 普通はこれが最善
    $AoA[$i] = \@array;         # 危険; just how my() was that 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 の記法を望むのなら、先頭のデリファレンス演算子 $ より前に $AoA[$i] を評価することを強制するために、${$AoA[$i]} と 記述しなければなりません


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

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

    #!/usr/bin/perl -w
    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]


DEBUGGING

5.002 より前の、標準の Perl デバッガは複雑なデータ構造をきちんと きれいに出力することができませんでした。 5.002 以降のデバッガでは複雑なデータ構造をダンプするための 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 "elt $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 "elt $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 { keys %{$HoH{$b}} <=> keys %{$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 { keys %{ $HoH{$b} } <=> keys %{ $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(1), perllol(1), perldata(1), perlobj(1)


AUTHOR

Tom Christiansen <tchrist@perl.com>

Last update: Wed Oct 23 04:57:50 MET DST 1996