| Top Page | プログラミング | Perl 目次 | prev | next | 索引 |

22. リファレンス:配列やハッシュをスカラーに?

いよいよ基礎講座の最後の一山,リファレンスの説明です. はじめはイメージが掴みにくいかもしれませんが,そこを克服し理解して 使いこなせるようになると,世界が大きく広がります. 無事の登頂を目指しましょう. リファレンスの勉強にあたっては,

という3点に注意しましょう.


リファレンスが欲しくなるとき

こんなデータがあるとします.実験条件が6通りで,それぞれの場合について 4回の測定データがあります.

L100N100 L100N010 L020N100 L020N010 L005N100 L005N010
10.5 5.5 8.1 5.1 3.2 3.1
10.8 5.2 8.8 4.5 3.1 2.9
12.5 7.1 6.6 4.8 2.5 2.6
10.5 6.8 7.9 4.5 3.6 3.2

こんな構造のデータを Perl の中で扱うにはどうするか. 実験条件の名前 ( L100N100 など ) をキー,測定データの配列を値とする ハッシュなんてのができると,とても便利そうです. でも,残念ながらそれはできません. ハッシュのそれぞれのキーに対応するのは,スカラーでなければいけないという 文法上の制約があるからです. 「文字列がキーで対応するのは配列」という構造のハッシュは作れません.

あるいは,3次元の座標データがたくさんあるとします.空間中を動物が 動き回った軌跡でもいいし,植物の枝先の座標でもいいでしょう.

 X   Y   Z 
0.5 -0.3 7.8
1.8 0.2 8.9
1.1 -1.5 5.3
2.1 0.1 3.5

X, Y, Z 座標という3つ一組のデータがたくさんという構造ですから, 「要素数3(X, Y, Z 座標)の配列」の配列が作れたら便利そうです.でも, 配列の個々の要素はスカラーでないといけないという文法上の制約があり, 残念ながら「配列の配列」は作れません.

もうひとつ例を出します. 2ケ所の調査地での生物相の調査データから,共通種を探し出したいとします. ほんとはデータをファイルから読み込むのがそれっぽくてよいのですが, ここは簡単のためにプログラムにじかに書きます.

# qw( ) については前のページで解説したばかり.

@species_list1 = qw(シーラカンス ヒメシーラカンス エゾシーラカンス);
@species_list2 = qw(ヒメシーラカンス ヤンバルシーラカンス ケシーラカンス);

&show_common(@species_list1, @species_list2);

sub show_common
{
    foreach $sp (@_) {    # まずは受けとった種名リストを表示
        print $sp, " ";
    }
    print "\n";

    # これから共通種を探して表示する処理(略)
}

これを実行してみると,こんな出力が得られます.

シーラカンス ヒメシーラカンス エゾシーラカンス ヒメシーラカンス ヤンバルシーラカンス ケシーラカンス 

サブルーチンには,それぞれ要素数が3の2つの配列を渡しているのですが, @_ には,それらの要素あわせて6個が並びます. これでは,どこまでが最初の配列の要素か分かりません.

「サブルーチンの引数はスカラーが並んだひとつのリスト」 という制約があって,2つの配列を渡しても,あわせたひとつのリストにされて しまうのです. これでは,2つの配列を受けとって,それらを比較しながら処理する, といったサブルーチンが書けません. ハッシュを渡すにも同様の困難があります.


うえにあげたの3つの例では,

という,スカラーしか許されない制約ゆえの不自由さを説明しました. もし,「配列を表すスカラー変数」とか,「ハッシュを表すスカラー変数」 といったものがあって,あとから必要に応じて「もとの配列」だの 「もとのハッシュ」だのが取り出せるなら,これらの不自由は克服できます. それを可能にするのがリファレンスです.

リファレンスは,スカラー変数にしろ配列変数にしろ ハッシュ変数にしろ,とにかくメモリ上に存在してる変数の「ありか」 のことです(具体的にはメモリーの番地です). 配列変数のありかを指し示すファレンスは配列へのリファレンス, ハッシュ変数のありかを指し示すリファレンスはハッシュへのリファレンス, スカラー変数のありかを指し示すリファレンスはスカラーへのリファレンス と呼びます.

リファレンス(reference)は参照という意味です.ほかの変数(メモリ上の領域) の番地を記録していて,その番地のところへ行けば変数が見つかります. 変数そのものでなく,「変数のありか」を示しているので, リファレンス(参照)と呼びます.

リファレンスは「変数のありか」というひとつの値のことですから, 当然スカラーです. したがってスカラー変数にリファレンスをしまっておくことができます.

リファレンスがスカラーならば,リファレンスの配列も作れるだろうし, ハッシュの値としてリファレンスを覚えさせることもできそうだし, サブルーチンにふたつの配列のリファレンスを渡して,それぞれの配列を区別して 処理することもできそうです.


リファレンスの作り方,使い方

リファレンスの作り方と使い方は,プログラム例で説明しましょう.

@x = (10, 20, 30);

#  配列名の前に \ を付けると,配列へのリファレンスが得られる.
#  リファレンスはスカラーだから,スカラー変数に代入できる.
$ref_x = \@x;  

# 配列へのリファレンスの前に @ をつけると,もとの配列が得られる.
foreach $data (@$ref_x) {  # @$ref_x は @x と同じ
    print $data, " ";
}
print "\n";

# 配列へのリファレンスの前に $, うしろに […] をつけると,もとの配列の要素が得られる

$n_data = @x;
for ($i = 0; $i < $n_data; ++$i) {
    print $$ref_x[$i], " "; # $$ref_x[$i] は $x[$i] と同じ
}
print "\n";

うえのプログラムを,コメントも含めてじっくり読んでください. 要点を整理しておきます.


ハッシュでも同様にリファレンスを作ることができます.

%mountain_heights = (Fujisan =>    3776,   # この初期化法は前のページで解説した
                     Kitadake =>   3192,
                     Yarigatake => 3180,
                     Tsukubasan =>  876);  

# ハッシュ名の前に \ を付けると,ハッシュへのリファレンスが得られる.
$ref_heights = \%mountain_heights;

# ハッシュへのリファレンスの前に % をつけると,もとのハッシュになる.
# ハッシュへのリファレンスの前に $, うしろに {…} をつけると,
# もとのハッシュの値が得られる.

foreach $mountain (keys %$ref_heights) {
    print $mountain, "\t", $$ref_heights{$mountain}, "\n";
}

配列の場合と同じことなので,くどいようですが, 念のためにそのまま繰り返します.


配列やリストだけでなく,スカラー変数のリファレンスも同様に作れます. でも,配列やリストのリファレンスほどのありがたみはないので, 特に説明はしません.


リファレンスを介して変数の値を変える

リファレンスは,メモリ上に実在する変数のありかを指し示しています. だから,リファレンスを介してその変数の値を変えることができます. 配列のリファレンスを例に説明します.

@x = (10, 20, 30);

$ref_x = \@x;  # $ref_x は,配列 @x へのリファレンス  

$n_data = @x;
for ($i = 0; $i < $n_data; ++$i) {
    $$ref_x[$i] += 100;  # リファレンスを介して @x の各要素に 100 を加える.
}

for ($i = 0; $i < $n_data; ++$i) {
    print $x[$i], " ";   # @x の各要素を出力する.
}
print "\n";

このプログラムを実行すると,こんな出力が得られます.

110 120 130 

リファレンスを介して要素の値をいじると,ちゃんと配列 @x の 要素の値が変っています.

このプログラムは,こう書いても同じ動作をします.

@x = (10, 20, 30);

$ref_x = \@x;  # $ref_x は,配列 @x へのリファレンス  

foreach $data (@$ref_x) {  #  @$ref_x は @x のこと(コピーでなく,@x そのもの)
    $data += 100;          # 各要素に 100 を加える.
}

foreach $data (@$ref_x) {
    print $data, " ";
}
print "\n";

当然ながら,配列 @x へのリファレンス $x_ref を作製したあと, このリファレンスを介さずに @x の内容を書き換えたら,そのあと リファレンス $x_ref を介して見える @x の内容は新しいものになっています.

@x = (10, 20, 30);

$ref_x = \@x;                    # $ref_x は,配列 @x へのリファレンス  

@x = (100, 200, 300, 400, 500);  # @x の内容を書き換える.

foreach $data (@$ref_x) {        # $ref_x を介して @x の要素を表示.
    print $data, " ";
}
print "\n";

このプログラムを実行すると,変更後の @x が表示されます.

100 200 300 400 500 

リファレンスはもとの配列の複製ではない,もとの配列を指し示しているだけだ, ということを忘れないようにしましょう.


<練習>

もしむずかしかったら,まずはリファレンスを使わないで書いてみて, それからリファレンスを使うように書き換えてみましょう.

このあとのページでリファレンスの活用例を紹介しますが, まずはこのページの内容をじっくり理解してください. 説明用プログラムはぜひ動作を確認してください. また,上の練習も試してみてください.



| Top Page | プログラミング | Perl 目次 | prev | next |