5.22.1

NAME

perldsc - Perl Data Structures Cookbook

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

説明

Perl lets us have complex data structures. You can write something like this and all of a sudden, you'd have an array with three dimensions!

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

Alas, however simple this may appear, underneath it's a much more elaborate construct than meets the eye!

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

How do you print it out? Why can't you say just print @AoA? How do you sort it? How can you pass it to a function or get one of these back from a function? Is it an object? Can you save it to disk to read back later? How do you access whole rows or columns of that matrix? Do all the values have to be numeric?

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

As you see, it's quite easy to become confused. While some small portion of the blame for this can be attributed to the reference-based implementation, it's really more due to a lack of existing documentation with examples designed for the beginner.

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

This document is meant to be a detailed but understandable treatment of the many different sorts of data structures you might want to develop. It should also serve as a cookbook of examples. That way, when you need to create one of these complex data structures, you can just pinch, pilfer, or purloin a drop-in example from here.

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

Let's look at each of these possible constructs in detail. There are separate sections on each of the following:

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

  • arrays of arrays

    (配列の配列)

  • hashes of arrays

    (配列のハッシュ)

  • arrays of hashes

    (ハッシュの配列)

  • hashes of hashes

    (ハッシュのハッシュ)

  • more elaborate constructs

    (より手の込んだ構造)

But for now, let's look at general issues common to all these types of data structures.

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

リファレンス

The most important thing to understand about all data structures in Perl--including multidimensional arrays--is that even though they might appear otherwise, Perl @ARRAYs and %HASHes are all internally one-dimensional. They can hold only scalar values (meaning a string, number, or a reference). They cannot directly contain other arrays or hashes, but instead contain references to other arrays or hashes.

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

You can't use a reference to an array or hash in quite the same way that you would a real array or hash. For C or C++ programmers unused to distinguishing between arrays and pointers to the same, this can be confusing. If so, just think of it as the difference between a structure and a pointer to a structure.

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

You can (and should) read more about references in perlref. Briefly, references are rather like pointers that know what they point to. (Objects are also a kind of reference, but we won't be needing them right away--if ever.) This means that when you have something which looks to you like an access to a two-or-more-dimensional array and/or hash, what's really going on is that the base type is merely a one-dimensional entity that contains references to the next level. It's just that you can use it as though it were a two-dimensional one. This is actually the way almost all C multidimensional arrays work as well.

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

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

Now, because the top level contains only references, if you try to print out your array in with a simple print() function, you'll get something that doesn't look very nice, like this:

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

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

That's because Perl doesn't (ever) implicitly dereference your variables. If you want to get at the thing a reference is referring to, then you have to do this yourself using either prefix typing indicators, like ${$blah}, @{$blah}, @{$blah[$i]}, or else postfix pointer arrows, like $a->[3], $h->{fred}, or even $ob->method()->[3].

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

よくある間違い

The two most common mistakes made in constructing something like an array of arrays is either accidentally counting the number of elements or else taking a reference to the same memory location repeatedly. Here's the case where you just get the count instead of a nested array:

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

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

That's just the simple case of assigning an array to a scalar and getting its element count. If that's what you really and truly want, then you might do well to consider being a tad more explicit about it, like this:

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

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

Here's the case of taking a reference to the same memory location again and again:

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

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

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

So, what's the big problem with that? It looks right, doesn't it? After all, I just told you that you need an array of references, so by golly, you've made me one!

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

Unfortunately, while this is true, it's still broken. All the references in @AoA refer to the very same place, and they will therefore all hold whatever was last in @array! It's similar to the problem demonstrated in the following C program:

残念なことに、これは正しいのですがまだおかしいのです。 @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);
    }

Which will print

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

    daemon name is daemon
    root name is daemon

The problem is that both rp and dp are pointers to the same location in memory! In C, you'd have to remember to malloc() yourself some new memory. In Perl, you'll want to use the array constructor [] or the hash constructor {} instead. Here's the right way to do the preceding broken code fragments:

この問題は、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 ];
    }

The square brackets make a reference to a new array with a copy of what's in @array at the time of the assignment. This is what you want.

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

Note that this will produce something similar, but it's much harder to read:

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

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

Is it the same? Well, maybe so--and maybe not. The subtle difference is that when you assign something in square brackets, you know for sure it's always a brand new reference with a new copy of the data. Something else could be going on in this new case with the @{$AoA[$i]} dereference on the left-hand-side of the assignment. It all depends on whether $AoA[$i] had been undefined to start with, or whether it already contained a reference. If you had already populated @AoA with references, as in

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

    $AoA[3] = \@another_array;

Then the assignment with the indirection on the left-hand-side would use the existing reference that was already there:

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

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

Of course, this would have the "interesting" effect of clobbering @another_array. (Have you ever noticed how when a programmer says something is "interesting", that rather than meaning "intriguing", they're disturbingly more apt to mean that it's "annoying", "difficult", or both? :-)

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

So just remember always to use the array or hash constructors with [] or {}, and you'll be fine, although it's not always optimally efficient.

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

Surprisingly, the following dangerous-looking construct will actually work out fine:

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

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

That's because my() is more of a run-time statement than it is a compile-time declaration per se. This means that the my() variable is remade afresh each time through the loop. So even though it looks as though you stored the same variable reference each time, you actually did not! This is a subtle distinction that can produce more efficient code at the risk of misleading all but the most experienced of programmers. So I usually advise against teaching it to beginners. In fact, except for passing arguments to functions, I seldom like to see the gimme-a-reference operator (backslash) used much at all in code. Instead, I advise beginners that they (and most of the rest of us) should try to use the much more easily understood constructors [] and {} instead of relying upon lexical (or dynamic) scoping and hidden reference-counting to do the right thing behind the scenes.

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

In summary:

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

    $AoA[$i] = [ @array ];     # usually best
    $AoA[$i] = \@array;        # perilous; just how my() was that array?
    @{ $AoA[$i] } = @array;    # way too tricky for most programmers
    $AoA[$i] = [ @array ];     # 普通はこれが最善
    $AoA[$i] = \@array;        # 危険; just how my() was that array?
    @{ $AoA[$i] } = @array;    # ほとんどのプログラマには技巧的過ぎ

優先順位に関する警告

Speaking of things like @{$AoA[$i]}, the following are actually the same thing:

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

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

That's because Perl's precedence rules on its five prefix dereferencers (which look like someone swearing: $ @ * % &) make them bind more tightly than the postfix subscripting brackets or braces! This will no doubt come as a great shock to the C or C++ programmer, who is quite accustomed to using *a[i] to mean what's pointed to by the i'th element of a. That is, they first take the subscript, and only then dereference the thing at that subscript. That's fine in C, but this isn't C.

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

The seemingly equivalent construct in Perl, $$aref[$i] first does the deref of $aref, making it take $aref as a reference to an array, and then dereference that, and finally tell you the i'th value of the array pointed to by $AoA. If you wanted the C notion, you'd have to write ${$AoA[$i]} to force the $AoA[$i] to get evaluated first before the leading $ dereferencer.

Perlにおける等価な構造の $$aref[$i] では、最初に $aref の デリファレンスをして、$aref から配列への参照にします; そしてその デリファレンスをして、最後に $AoA によって指されている配列の i番目 の値を 取り出します。 もし C の記法を望むのなら、先頭のデリファレンス演算子 $ より前に $AoA[$i] を評価することを強制するために、${$AoA[$i]} と 記述しなければなりません

WHY YOU SHOULD ALWAYS use strict

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

If this is starting to sound scarier than it's worth, relax. Perl has some features to help you avoid its most common pitfalls. The best way to avoid getting confused is to start every program like this:

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

    #!/usr/bin/perl -w
    use strict;

This way, you'll be forced to declare all your variables with my() and also disallow accidental "symbolic dereferencing". Therefore if you'd done this:

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

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

    print $aref[2][2];

The compiler would immediately flag that as an error at compile time, because you were accidentally accessing @aref, an undeclared variable, and it would thereby remind you to write instead:

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

    print $aref->[2][2]

デバッグ

You can use the debugger's x command to dump out complex data structures. For example, given the assignment to $AoA above, here's the debugger output:

複雑なデータ構造をダンプするために、デバッガの 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'

コード例

Presented with little comment (these will get their own manpages someday) here are short code examples illustrating access of various types of data structures.

ここにあるちょっとしたコメント(将来は独立したマニュアルページと なるでしょう)は、様々な形式のデータ構造へのアクセスを 例示するサンプルです。

配列の配列

配列の配列の宣言

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

もっと複雑なレコード

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

Here's a sample showing how to create and use a record whose fields are of many different sorts:

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

     $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, },
            ],
         },
      );

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

     # reading from file
     # this is most easily done by having the file itself be
     # in the raw data format as shown above.  perl is happy
     # to parse complex data structures if declared as data, so
     # sometimes it's easiest to do that
     # ファイルからの読み込み
     # これはファイルそれ自身が、先の例で示したように raw data
     # format になっているのでとても簡単です。perlは、データのよ
     # うに宣言されているのであれば、複雑なデータ構造を喜んで解
     # 析します; ですからとても簡単に済むことがあるのです
     # here's a piece by piece build up
     $rec = {};
     $rec->{series} = "flintstones";
     $rec->{nights} = [ find_days() ];
     # フィールド毎に構築する
     $rec = {};
     $rec->{series} = "flintstones";
     $rec->{nights} = [ find_days() ];
     @members = ();
     # assume this file in field=value syntax
     while (<>) {
         %fields = split /[\s=]+/;
         push @members, { %fields };
     }
     $rec->{members} = [ @members ];
     @members = ();
     # このファイルは フィールド=値 という構文となっていると仮定
     while (<>) {
         %fields = split /[\s=]+/;
         push @members, { %fields };
     }
     $rec->{members} = [ @members ];
     # now remember the whole thing
     $TV{ $rec->{series} } = $rec;
     # now remember the whole thing
     $TV{ $rec->{series} } = $rec;
     ###########################################################
     # now, you might want to make interesting extra fields that
     # include pointers back into the same data structure so if
     # change one piece, it changes everywhere, like for example
     # if you wanted a {kids} field that was a reference
     # to an array of the kids' records without having duplicate
     # records and thus update problems.
     ###########################################################
     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 ];
     }
     ###########################################################
     # ここで、あなたは同じデータへの戻るポインターのような興味
     # 深い追加フィールドを作成したいと思うかもしれません; これ
     # により、ある一つを変更するとすべてが変更されます; この例
     # で言えば、{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 ];
     }
     # you copied the array, but the array itself contains pointers
     # to uncopied objects. this means that if you make bart get
     # older via
     # あなたは配列をコピーしましたが、配列それ自身はコピー
     # されていないオブジェクトへのポインターから構成されています。
     # これはあなたがbartを作ったら古いものを通してで取得され
     # るということです
     $TV{simpsons}{kids}[0]{age}++;
     $TV{simpsons}{kids}[0]{age}++;
     # then this would also change in
     print $TV{simpsons}{members}[2]{age};
     # そしてこれは変更されたものです
     print $TV{simpsons}{members}[2]{age};
     # because $TV{simpsons}{kids}[0] and $TV{simpsons}{members}[2]
     # both point to the same underlying anonymous hash table
     # なぜなら $TV{simpsons}{kids}[0] と $TV{simpsons}{members}[2]
     # は両方とも同じ無名のハッシュテーブルを指しているからです
     # print the whole thing
     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";
     }
     # 全体を表示する
     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

You cannot easily tie a multilevel data structure (such as a hash of hashes) to a dbm file. The first problem is that all but GDBM and Berkeley DB have size limitations, but beyond that, you also have problems with how references are to be represented on disk. One experimental module that does partially attempt to address this need is the MLDBM module. Check your nearest CPAN site as described in perlmodlib for source code to MLDBM.

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

SEE ALSO

perlref, perllol, perldata, perlobj

作者

Tom Christiansen <[email protected]>