リファレンスの利用方法
Perlにはスカラや配列、ハッシュのほかに、『リファレンス』という一風変わった変数があります。スカラに代表される一般的な変数は、その名のとおりデータ(値)を記録するためのものですが、リファレンスは違います。リファレンスは値を記録する代わりに、値を格納しているアドレスを記録します。
リファレンスの概要
ここでちょっと変数についておさらいしてみましょう。変数は、ユーザが指定した値を記録し、あとから変数名を使ってその値を呼び出すためのものです。このとき、変数が何をやっているかというと、指定された値をコンピュータのメモリに記録して、アドレスと変数名をリンクさせています。
リファレンスは、値の代わりに、変数名にリンクされたアドレスを記録するスカラ変数です。アドレスを記録するという性質に、どのようなメリットがあるかは以下のとおりです。
- サブルーチンに渡す引数に、複数の配列やハッシュを指定可能になる
- サブルーチンの戻り値として、変数の値を渡すよりも、変数のリファレンスを渡すほうが高速
- 2次元配列などの複雑なデータ構造を作成できる
リファレンス演算子(\)を使ったリファレンスの作成
リファレンスの作成で最もシンプルなのは、変数に対してリファレンス演算子(\)を使う方法です。
$word = "A"; # $word に値を代入
$ref_word = $word; # $word へのリファレンス
変数 $word へのリファレンスとして変数 $ref_word を作成した場合、変数 $ref_wordは、変数 $word の値ではなく、そのアドレスを記録します。
リファレンス演算子は、配列やハッシュなど、どのような種類の変数にも有効です。
# 配列のリファレンスを作成
$ref_array = \@list;
# ハッシュのリファレンスを作成
$ref_hash = \%hash;
# 関数のリファレンスを作成
$ref_sub = \⊂
# グロブのリファレンスを作成
$ref_glob = \*glob;
配列とハッシュはスカラ変数の集まりですから、個々の要素へのリファレンスも作成できます。
# 配列の要素へのリファレンスを作成
$ref_list_el = $list[0];
# ハッシュの要素へのリファレンスを作成
$ref_hash_el = $hash{'element'};
値に対してもリファレンスは有効です。値に対するリファレンスは、書き換え不可の変数として扱えます。
$ref = .14; # $ref は読み込み専用
print $ref;
> 3.14
リファレンスの参照
リファレンスから変数の値を参照するには、参照先の変数の識別子を先頭に付け足します。
# スカラのリファレンスを作成
$word = "A";
$ref_word = $word;
print $ref_word, "\n";
> A
# 配列のリファレンスを作成
@array = (1,2,3,4,5);
$ref_array = \@array;
print @$ref_array;
> 12345
# ハッシュのリファレンスを作成
%hash = (element1=>"A", element2=>"B");
$ref_hash = \%hash;
while( ($key,$value) = each %$ref_hash ){
print $key, " : ", $value, "\n";
}
> element1 : A
> element2 : B
リファレンスへのリファレンスの場合は、単純にダラー( $ )を増やしていきます。
# スカラのリファレンスを作成
$word = "A";
$ref_word = $word;
# リファレンスへのリファレンスを作成
$ref_ref_word = $ref_word;
print $$ref_ref_word, "\n";
> A
ブロックを使っての参照以外にも、矢印演算子( -> )を使った参照があります。
# 無名ハッシュのリファレンスを作成
$ref_hash = {
'element1' => 'A'
};
print $ref_hash{'element1'}; # 一般的な構文
print $ref_hash->{'element1'}; # 矢印演算子
矢印演算子は、カッコつきの添え字の間にあるときは省略することができます。
# 次の2つの式は同じ意味です
$ref_data[0]->[0]->{A} = 3.14
$ref_data[0][0]{A} = 3.14
参照先のデータタイプを調べる
ref 演算子を使って、参照先のデータタイプを調べることができます。
$ref_a = ;
print ref $ref_a;
> SCALAR
$ref_b = sub{'code'};
print ref $ref_b;
> CODE
ソフトリファレンス
リファレンスの奇妙な特性として、まだリファレンスが生成されていない状態で、すでにあるものと仮定して参照することができます。これをソフトリファレンスと呼びます。この特性はあまり役立つ機会がありませんが、たとえば、次の例のように、リファレンス元の変数を用意することなく、直接リファレンスに値を渡し、あとから参照することが可能です。
$ref_val = 5; # この状態では $ref_val は存在していない
print "$ref_val"; # $ref_val が自動生成される
> 5
ソフトリファレンスの使用は、多少危険です。ハードリファレンスを使おうとした場合にも、誤ってシンボリックリファレンスを使ってしまうような場合があるからです。これを防止するには、次のような宣言をしておきます。
use strict 'refs';
use strict宣言以降では、ハードリファレンスのみが許されるようにすることができます。逆に、下記の宣言で打ち消すこともできます。
no strict 'refs';
シンボリックリファレンスでは、パッケージ変数だけを見ることができます。 (my() で宣言した)静的なローカル変数は、シンボルテーブルにありませんので、シンボリックリファレンスでは参照することができません。 たとえば:
local($value) = 10;
$ref = $value;
{
my $value = 20;
print $ref;
}
これは、20 ではなく、10 と出力します。 local()は、パッケージで「グローバルな」、パッケージ変数に影響するものです。
無名オブジェクトへのリファレンス
変数に保存されていないデータへのリファレンスも作れます。このデータは、変数名とのリンクがないので無名データと呼ばれます。
無名配列へのリファレンス
無名配列へのリファレンスは、ブラケット([ ])を使って作ることができます
$ref_array = [1, 2, 3, 4, 5];
print $ref_array->[0], "\n";
> 1
2. 名前の無い配列へのリファレンスは、大カッコを使って作ることができます:
$arrayref = [1, 2, ['a', 'b', 'c']];
ここでは、『「名前の無い 3 個の要素を持つ配列」を最後の要素として持つ 3個要素の名前の無い配列』へのリファレンスを作っています。あとで述べる多次元構文を使って、これをアクセスすることができます。たとえば、上記のようにした後では、$arrayref->[2][1]が "b" となります。
無名ハッシュへのリファレンス
無名ハッシュへのリファレンスは、ブレース( { } )を使って作ることができます。
$ref_hash = {
"element1" => "A",
"element2" => "B",
};
# 要素を追加
$ref_hash->{"element3"} ="C";
ブレースは無名ハッシュ以外にも使われますから、文の最初のブレースの前に + か returnをつけて、無名ハッシュでの使用を明確にしたほうがよい場合があります。
たとえば、新しいハッシュを作って、それへのリファレンスを返すサブルーチンが欲しければ、以下のようにします。
# 警告はありませんが、間違っている
sub hash_em { { @_ } }
# 下記の2つは同じ意味で、正しい表記
sub hash_em { +{ @_ } }
sub hash_em { return { @_ } }
無名サブルーチンへのリファレンス
無名サブルーチンのへのリファレンスは、サブルーチン名の無い sub を使って作ることができます:
$ref_code = sub { print "OK!\n" };
$ref_code->();
> OK!
&$ref_code;
> OK!
セミコロンがあることに注意してください。内部のコードが即座に実行されるのではないという事実を除いて、sub{}は、宣言というよりもむしろ、do{} や eval{} のような演算子です。しかし、eval("...")の中でなければ、何回その行を実行しようとも $coderefは、同一の無名サブルーティンを参照することになります。
こういったことを気にする方のため、現在のインプリメントでは、local()変数の浅い結び付きとなっています。my() 変数はアクセスできません。しかし、実行時の eval()を使えば、これに対応することができます:
{
my $x = time;
$coderef = eval "sub { $x }";
}
通常は、(sub {} だけをつかったり、eval{} をも使う場合にも) 新しい subはグローバルな $x をアクセスすることができるだけです。しかし、実行時に eval()を使っているので、呼ばれるごとに、新しいサブルーティンリファレンスを作り出すだけではなく、グローバルな変数の代わりに、プログラム上で自分より前に出てくるmy() 変数をアクセスすることが許されます。一般には、アクセスされる $x は、新しい subが作られるごとに、違うものとなります。 このしくみでは、変数は、動的な深い結び付きとなります。
配列などの値を利用して、無名サブルーチンを作成することも可能です。
@subs = qw(Yahoo Google Excite);
foreach my $key( @subs ){
${ $key } = sub { print "$key\n" };
}
&$Yahoo();
> Yahoo
次の例では、型グロブを使って無名サブルーチンを作成しています。型グロブによる無名サブルーチンの呼び出しは、普通のサブルーチンと同じになります。
@subs = qw(Yahoo Google Excite);
foreach my $key( @subs ){
*{ "print" . $key } = sub { print "$key\n" };
}
&printYahoo();
> Yahoo
リファレンスの便利な利用法
サブルーチンにリファレンスを渡す
サブルーチンに2個以上の配列やハッシュをまとまりを持ったまま受け渡しするには、リファレンスを使います。もっとも単純な方法は、リファレンス演算子を使って配列やハッシュを渡します。
# サブルーチン function1 に配列とハッシュのリファレンスを渡す
&function1(\@array, \%hash);
sub function1{
my ($ref_array, $ref_hash) = @_;
foreach ( @$ref_array ){ ... }
foreach ( keys %$ref_hash ){ ... }
}
配列やハッシュをリファレンスとして渡しておき、サブルーチン側で値のコピーを行うこともできます。
&function2(\@array, \%hash);
sub function2{
my $ref_array = shift; # 配列リファレンス
my %hash = %{shift()}; # ハッシュのコピーを作成
...
}
サブルーチンからリファレンスを返す
サブルーチンの戻り値にリファレンスを使うこともできます。この利点は、処理の高速化にあります。たとえば、あるサブルーチンで巨大なファイルデータを変数に詰め込み、その値を返すとなると、巨大なデータをコピーすることになります。それよりも、巨大になった変数のリファレンスを渡すだけのほうが、コピー処理がなくなる分だけ処理が早くなります。
# ファイル名を渡し、そのファイルの内容をハッシュリファレンスで受け取る
$ref_hash = &function1("file.txt");
sub function1{
my $file = shift;
open(FH, $file);
# ファイルの内容を %array に代入
my @array = <FH>;
close(FH);
return \@array; # 配列変数のアドレスを渡す
}
※function1が渡す@arrayはfunction1サブルーチンのローカル変数ですが、Perlでは問題なくその外側から参照することができます。
リファレンスで多重配列を作る
リファレンスを使って、リストのリストや、そのほかの複雑な構造を作成することができます。
# リストリファレンスのリストを配列に代入
@array = (
[1, 2],
[3, 4],
);
print $array[0][0];
> 1
# foreach を使ってすべての要素にアクセス
foreach $key1 ( @array ){
foreach $key2 ( @{ $key1 } ){
print $key2;
}
}
> 1234
# for を使ってすべての要素にアクセス
for $i ( 0 .. $#array ){
$ref = $array[$i];
for $j ( 0 .. $#{ $ref } ){
print $ref->[$j];
}
}
> 1234
上記はカッコでリスト全体を囲んでいます。配列へのリファレンスを作成するには、外側をブラケットにします。
# リストリファレンスのリストへのリファレンスをスカラ変数に代入
$ref_array = [
[1, 2],
[3, 4],
];
print $ref_array->[1][1];
> 4
ハッシュを使って配列を管理することも可能です。
%hash = (
element1 => [ 1, 2 ],
element2 => [ 3, 4 ],
element3 => [ 5, 6 ],
);
print $hash{"element1"}[0];
> 1
複雑なデータ構造を操作する
-map/grep
要素値がリファレンスになっている配列を扱う一番簡単な方法は、foreach を使います。
@ref_array = (
[1, 2],
[3, 4],
);
foreach ( @ref_array ){
push @x, $_->[0];
}
map を使った方法
map を使って処理した方が、高速です。
@x = map { $_->[0] } @ref_array; # @ref_arrayの各無名配列から0番目の要素を選択