1. ホーム
  2. スクリプト・コラム
  3. パール

Perlのハッシュの作成と参照入門

2022-02-03 22:20:08

構文
引用」を作成する方法と、それを使用する方法は2つだけです。

リファレンスを作成する

ルール1の作成
変数の前に「/」記号を付けると、その変数への「参照」が得られます。

    $aref = /@array; # $arefは@arrayへの「参照」を保持します。
    $href = /%hash; # $href は %hash への「参照」を保持します。
arefや$hrefのような変数に「参照」を保存すると、他のスカラーと同じようにコピーや保存ができるようになります。

    xy = $aref; # $xy は @array への「参照」を保持します。
    p[3] = $href; # $p[3] は %hash への '参照' を保持するようになりました。
    $z = $p[3]; # $z は %hash への '参照' を保持します。
これらの例では、名前の付いた変数への「参照」を作成する方法を示しましたが、時には名前のない配列やハッシュを作成することもあります。これは、変数に入れない文字列 '/n' や数字 '80' を使うときと似ています。

作成ルール2

[ITEMS ] は新しい無名配列を作成し、その配列への '参照' を返します。{ITEMS } は、新しい無名ハッシュを作成し、そのハッシュへの「参照」を返します。

    $aref = [ 1, "foo", undef, 13 ]。  
    # $aref はこの配列への「参照」を保持します。
    $href = { APR =>; 4, AUG =>; 8 }.   
    # $href はこのハッシュへの「参照」を保持します。
ルール2からの「参照」とルール1からの「参照」は、同じ型である。

        # ここで
        $aref = [ 1, 2, 3 ]。
        # 上と同じです。
        @array = (1, 2, 3)です。
        $aref = /@array;
最初のメソッドは、余分な配列変数 @array を作成しないことを除いて、次の2行を省略したものです。

記号[]を書くだけなら、新しい空の無名配列が得られます。記号 {} を使用すると、新しい、空の匿名ハッシュが得られます。

リファレンスを使用する

参照」を作成すると、その参照で何ができるのでしょうか?それはスカラーであり、他のスカラーと同じように保存したり取り出したりすることができます。それ以外に、2つの使い方がある。


使用ルール1
配列の名前を中括弧で囲んだ配列の'参照'に置き換えることはいつでもできます。例えば、@arrayの代わりに@{$aref}を使用します。

以下は使用例です。

配列です。

        a @{$aref} 配列です。
        reverse @a reverse @{$aref} 配列を逆順に並べ替え
        $a[3] ${$aref}[3] 配列のメンバ
        $a[3] = 17; ${$aref}[3] = 17 メンバーに値を代入する
上の各行で、2つの式は同じ関数を実装しています。左側の式は配列 @a を操作し、右側の式は「参照」 $aref が指す配列を操作します。これらは、配列に対して同じ効果をもたらします。

ハッシュの「参照」を使うことは、配列の「参照」を使うことと全く同じです。

        h %{$href} A ハッシュ
        keys %h keys %{$href} キーを取り出すハッシュです。
        $h{'red'} ${$href}{'red'} ハッシュのメンバーです。
        $h{'red'} = 17 ${$href}{'red'} = 17 メンバーに値を代入する
参照」を使って何をしたいのか、その方法は使用規則1がすでに教えてくれています。普通の配列やハッシュのようにPerlのコードを書いて、配列やハッシュの名前を{$reference}に置き換えるだけです。'参照'が1つしかないときに配列全体をたどるにはどうしたらいいか?' こんな風に書くんだ。

        for my $element (@array) {...
           ...
        }
そして、配列名@arrayを'reference'に置き換えてください。

        for my $element (@{$aref}) {.
           ...
        }
'参照'しかないのにハッシュの中身を表示するには?' まず、ハッシュ全体を表示するコードを書いてください。

        for my $key (キー %hash) { (キー %hash)
          print "$key =>; $hash{$key}/n";
        }
そして、そのハッシュの名前を'quote'で置き換えてください。

        for my $key (キー %{$href}) {...
          print "$key =>; ${$href}{$key}/n";
        }

使用ルール2
ルール1を使うことは、本当に必要なことです。なぜなら、それは「参照」をどのように処理するかを示しており、ほとんどすべての「参照」に対して機能するからです。しかし、私たちが通常行うのは、配列やハッシュの1つのメンバーだけを扱うことであり、ルール1を使うのは不便な方法なので、簡単にできる方法があります。

${$aref}[3]は読みにくいので、$aref->[3]のように記述します。

${$href}{red}と書くと扱いにくいので、$href->{red}のように書きます。

もし$arefが配列への「参照」を保持しているならば、$aref->[3]はその配列の4番目のメンバーです。全く別の配列の4番目のメンバーを表す$aref[3]と混同しないように、紛らわしい配列は@arefとします。変数$arefと@arefは、$itemと@itemと同じように、全く無関係です。

同様に、$href->{'red'}は名前のないハッシュであるにもかかわらず、ハッシュによって「参照」される変数$hrefの一部となっています。そして、$href{'red'}は、別の紛らわしい名前のハッシュである%hrefの一部となっています。記号 ' ->' を書くのを忘れがちで、そうなるとプログラムがデータを取り出したくない配列やハッシュからメンバーを取り出したときに、変な計算結果になってしまうのです。


例を見てみましょう。

まず、[1, 2, 3] は (1, 2, 3) を含む無名配列を作成し、その配列への「参照」を返すことを覚えておいてください。

では、考えてみましょう。

        @a = ( [1, 2, 3],
               [4, 5, 6],
               [7, 8, 9]
             );
aは3つのメンバーを持つ配列で、それぞれが別の配列への「参照」となっています。

a[1]はこれらの「参照」の1つです。これは (4, 5, 6) を含む配列を指しており、これは配列への「参照」なので、ルール 2 を使用すると、この配列の 3 番目のメンバーを取得するには $a[1]->[2] のように記述するように指示されます。  同様に、$a[0]->[1]の値は2です。ここでは2次元配列を使っていますが、$a[ROW]->[COLUMN]を使えば、配列の任意の行の任意の列のメンバーを取得または設定することが可能です。

これらの記号はまだ少し面倒に思えるので、もっと簡単な使い方があります。

矢印記号のルール
2つの添え字の間の矢印は任意である。

a[1]->[2]の代わりに$a[1][2]と書いても同じである。a[0]->[1] = 23に対して、$a[0][1] = 23とこのように書きます;これらも同じことです。

これで本当に2次元配列に見えるようになりましたね。

なぜ矢印が重要なのか、おわかりいただけると思います。矢印がなければ、$a[1][2]の代わりに${$a[1]}[2]と書かなければならないのです。3次元配列の場合は、読みにくい${${$x[2]}[3]}[5]の代わりに、$x[2][3][5]と簡単に書けるようになるんだ。

解決方法
ここで、先に提示した問題の解決策として、都市名と国名を再フォーマットすることについて説明します。

コピーコード コードは以下の通りです。

       my %table;
       while (<>) {
        chomp;
         my ($city, $country) = split /, /;
         $table{$country} = [] unless exists $table{$country};
         push @{$table{$country}}, $city;
       }
       foreach $country (sort keys %table) {
         print "$country: ";
        my @cities = @{$table{$country}};
        print join ', ', sort @cities;
        print ". /n";
      }

このプログラムは2つのパートに分かれています。2行目--7行目でデータの入力とデータ構造の作成が完了します。8行目から13行目までは、このデータを解析してレポートを出力する。ハッシュ %table を設定し、そのキーは国名で、堅牢な値はこの国名に対応する都市名の配列への「参照」である。このデータ構造は次のようなものである。

           テーブル
        +-------+--+   
        | |+-----------+--------+
        ドイツ| *---->|フランクフルト|ベルリン| |。
        | | +-----------+--------+
        +-------+--+
        | |+----------+
        |フィンランド| *---->|ヘルシンキ| [詳細
        | |+----------+
        +-------+--+
        | |+---------+------------+----------+
        | アメリカ| *---->|シカゴ|ワシントン|ニューヨーク|...
        | | +---------+------------+----------+
        +-------+--+
まず、出力の部分から分析しましょう。構造はすでにできているとして、アウトプットはどのようにすればいいのでしょうか?

コピーコード コードは以下の通りです。

       foreach $country (sort keys %table) {
         print "$country: ";
        my @cities = @{$table{$country}};
        print join ', ', sort @cities;
        print ". /n";
      }

tableは通常のハッシュで、ここからキーの列を取得し、キーをソートし、すべてのキーに対して反復処理を行うことができます。ここで使われている「参照」は10行目だけです。 $table{$country}は、ハッシュのキー$countryを調べてその値を取得しています。このキーの値は、対応する国の都市の配列への「参照」である。利用規則1によれば、@{$table{$country}}を使えば、配列全体を復元することができるのです。10行目は次のようになります。

        cities = @array。
違いは、ここでは配列の名前が「参照」{$table{$country}}に置き換えられていることです。シンボル@はPerlに配列全体を取得するように指示しています。都市のリストが得られたら、いつものようにソートして、都市名を結合し、プリントアウトします。

2〜7行目は、以下のようにデータ構造の作成を担当しています。

コピーコード コードは以下の通りです。

       while (<>) {
        chomp;
         my ($city, $country) = split /, /;
         $table{$country} = [] unless exists $table{$country};
         push @{$table{$country}}, $city;
       }

2~4行目では都市名と国名を取得します。そうでない場合、プログラムはシンボル[]を使って新しい空の匿名配列を作成し(作成規則2)、匿名配列への「参照」をハッシュの健全な値として置きます。

6行目では、都市名を対応する配列に入れます。table{$country}は、対応する国の都市の配列への「参照」を保持するようになりました。6行目は次のようになります。

        push @array, $city;
違いは、ここでは配列の名前を{$table{$country}}に置き換えている点です。pushコマンドは、この「参照」が指す配列の末尾に都市名を追加します。

ここで一点、見落としていたことがあります。5行目は不要です。取り除けばいいのです。

コピーコード コードは以下の通りです。

       while (<>) {
        chomp;
         my ($city, $country) = split /, /;
       #### $table{$country} = [] unless exists $table{$country};
         push @{$table{$country}}, $city;
       }

もし、ハッシュ %table にこの国名 $country のレコードがすでにあれば、5行目を追加してもしなくても変わりはない。6行目は、「参照」である$table{$country}が指す配列に自分自身を位置付け、値$cityを配列に入れる。しかし、ギリシャのように%tableにそのキーが存在しない場合はどうするのでしょうか?

これはPerlで、それだけで仕事をきっちりこなしてくれます。存在しない配列にアテネを代入したいので、Perlは新しい空の無名配列を作り、それをハッシュ%tableに入れ、その配列にアテネという値を入れるのを助けてくれる。これは「自動生成」と呼ばれるもので、物事が自分で生成されるようにすることです。Perlはキーがハッシュにないことを発見し、自動的に新しいハッシュ・レコードを作成します。Perlはハッシュの健全な値として配列を使いたいことを知ると、自動的に無名の空の配列を作り、その配列への「参照」をそのハッシュに入れる。一般にPerlは、この新しい都市名を保持するために、メンバーサイズが1つのみの配列を作成する。


その他のセット
10%の詳細で90%の利益を得ることを約束しました。つまり、90%の詳細な知識を省略したのです。今、その重要な部分を見てください。これは、100%の詳細について述べているマニュアルthe perlref manpageを読むよりずっと簡単です。

マニュアルperlrefのmanpageからいくつかのハイライトを紹介します。

スカラー、関数など、あらゆるものへの「参照」を作成することができます。 

使用法ルール1では、$arefのようなスカラー変数の中にある場合は、中括弧を省略することができます。例えば、@$arefは@{$aref}と同じであり、$$aref[1]は${$aref}[1]と同じである。初心者の方は、中括弧をつける習慣をつけるとよいでしょう。 

以下の操作では、「参照」が指す配列はコピーされません。 
        $aref2 = $aref1;
同じ配列を指す2つの「参照」を得ることができます。もし $aref1->[23] の値を変更すれば、変数 $aref2->[23] を見たとき、それに応じて変更されます。

この配列をコピーするには、次のようにします。

        $aref2 = [@{$aref1}];
記号 [...] を使って新しい無名配列を作成し、この新しい配列への「参照」を $aref2 に代入します。この新しい配列は、'参照' $aref1 が指す配列の内容で初期化されます。

同様に、匿名ハッシュをコピーするには、次のようにします。

        $href2 = {%{$href1}} です。

変数が「参照」されている内容を保持しているかどうかを判断するには、関数 ref を使用します。その引数が「参照」である場合、返される値は「真」です。この関数は、実際にはよりよい仕事をします。ハッシュへの参照であれば「HASH」、配列への参照であれば「ARRAY」を返します。 

参照」を文字列のように使いたい場合は、次のような文字列が得られます。 
        ARRAY(0x80f5dec)またはHASH(0x826afc0)
このような文字列が表示された場合、間違って「引用符」を出力していることを知る必要があります。

もうひとつ、この表示ではeqを使って2つの「参照」を比較し、同じものを指しているかどうかを確認することができます。(通常は == を使って比較することができます。)


文字列は「参照」と同じように使うことができます。配列への「参照」として "foo" を使用すると、配列 @foo への参照となります。これを「ソフトリファレンス」または「シンボリックリファレンス」と呼びます。これを無効にするには、use strict 'refs' という宣言を使います。誤って使ってしまうと、さまざまなエラーが発生する可能性があります。 

perlrefのマニュアルよりもperllolのマニュアルページを見たほうがいいかもしれません。これはデータ構造のクックブックで、ハッシュ化された配列、配列のハッシュ、その他のデータ構造を扱うメソッドを提供しています。