プログラミング > Perl > リファレンス


リファレンスの例
use v5.30.0;

my $str = "ABC to Z";
my @ary = (1,2,3);
my %hash = ( a => 1, b => 2);
sub code { "hello!" }
open(my $fh, '<', "/dev/null") or die "Could not open /dev/null for reading";

foreach my $ref(\$str, \@ary, \%hash, \&code, qr/[A-Z]/, \*str, \$fh) {
    my $rtype = ref $ref;
    if ($rtype eq "SCALAR") {    say qq($rtype: $$ref ) }
    elsif ($rtype eq "ARRAY") {  say qq($rtype: @$ref ) }
    elsif ($rtype eq "HASH") {   say qq($rtype: @{[ %$ref ]} ) }
    elsif ($rtype eq "CODE") {   say qq($rtype: @{[ &$ref ]} ) }
    elsif ($rtype eq "Regexp") { say qq($rtype: @{[ $$ref ]} ) }
    elsif ($rtype eq "GLOB") {   say qq($rtype: @{[ *$ref ]} ) }
    elsif ($rtype eq "REF")  {   say qq($rtype: @{[ *$$ref ]} ) }
    else                     {   say qq($rtype: ) }
}

close $fh;
実行結果
ARRAY(0x560a821949b8) ARRAY(0x560a821a2688)
SCALAR: ABC to Z 
ARRAY: 1 2 3 
HASH: a 1 b 2 
CODE: hello! 
Regexp: (?^u:[A-Z]) 
GLOB: *main::str  
REF: *main::$fh

多次元配列

編集

配列の中に配列が入っているものを多次元配列といいます。

Perlで多次元配列を生成するには、配列を要素にしたリストで配列を初期化すると・・・

配列を要素にしたリストで配列を初期化
#!/usr/bin/perl
use v5.30.0;
use warnings;

my @x = qw(A B C);
my @y = qw(X Y Z);
my @ary = ( @x, @y );

say "@ary"
実行結果
A B C X Y Z
@xと@yが展開され、単純な配列(一次元配列)になっています。
配列へのリファレンスを要素にしたリストで配列を初期化
#!/usr/bin/perl
use v5.30.0;
use warnings;

my @x = qw(A B C);
my @y = qw(X Y Z);
my @ary = ( \@x, \@y );

say "@ary";
say "@$_" foreach @ary
実行結果
ARRAY(0x560a821949b8) ARRAY(0x560a821a2688)
A B C 
X Y Z
\@xのように、配列の接頭辞「@」の前に「\」を付けます。

最初に示した例では、配列をリストの要素にしようとしましたが、Perlのリストは与えられた複合的なデータ構造を展開する性質があります。 この性質は、Perlの配列はコピーや関数やサブルーチンの引数として渡すときに(参照わたしでなく)値わたしであることと関連しています。 Perlの配列の代入は、他の多くの動的な型を持つプログラミング言語と違い、別名の作成ではなく要素ごとのコピーです。

リストは与えられた複合的なデータ構造を展開する
#!/usr/bin/perl
use v5.30.0;
use warnings;

my @x = ("A", "B", "C");
my %y = (a => 1, b => 2);
my @ary = (@x, %y);
say "@ary";
say join ", ", @ary
実行結果
A B C b 2 a 1 
A, B, C, b, 2, a, 1
多次元配列のような複雑な構造を作るためには、\@xとして、@xという配列を間接的に指示すことで実現します。

リファレンス

編集

リファレンスとは、あるデータが格納されている場所を指し示すデータ型です。データに「\」を前置して生成します。

#!/usr/bin/perl
use v5.30.0;
use warnings;

my $x = 42;
my $y = \$x;
say $y;
say $$y;
say ref $y;

$$y = 4423;
say $$y;
say $x;

my @ary = qw(1 2 4 8);
my $ref = \$ary[1];
say "@ary";
$$ref = 0;
say "@ary";
実行結果
SCALAR(0x5633528ba878)
42
SCALAR
4423
4423
1 2 4 8 
1 0 4 8
SCALARというのは、$xのデータ型がスカラーであることを表しています。
(0x55c487fbf9b8)は、$xが格納されている場所(メモリアドレス)を表しています。
なお、0x55c487fbf9b8という数値は環境によって異なります。
リファレンス$yを使って値を参照するには、参照先のオブジェクトのデータ型に対応した接頭辞(この場合はスカラーなので \)を補い$$yとします。
組込み関数 ref はリファレンス先のデータ型を返し、リファレンスでないものを引数にすると undef がかえります。
$y$xのリファレンスな状態で、$$yを書換えると$xが書換わります。
配列の要素へのリファレンスを取り、リファレンスを経由して配列(の要素)を参照/変更をすることもできます。
リファレンスはこのように、「間接的に値を参照する」仕組みであり、副次的な効果として「別名関係」を作ることになります。

スカラーだけでなく、様々なデータ型のリファレンスを生成することができます。

\42; # スカラーのリファレンス
\$x; # スカラーのリファレンス
\@x; # 配列のリファレンス
\%x; # ハッシュのリファレンス
\&x; # サブルーチンのリファレンス
\*x; # 型グロブのリファレンス
\\$x; # スカラーのリファレンスのリファレンス
「\」はリファレンスを生成するための単項演算子です。リファレンスそのものはスカラーの一種です。
$@%などの接頭辞を前置してリファレンスから元のデータを取り出すことを、デリファレンスといいます。
リファレンスはC言語のポインタに似ていますが、より抽象的で安全だとされています。
とはいえ、スコープを外れたオブジェクトのリファレンスを使った参照をすることができるなど、フリーハンドな安全が手に入るわけではありません。
リファレンスは、配列の配列などの複雑なデータ構造を扱う場合や、オブジェクトを扱う場合、サブルーチンに参照渡しを行う場合などに使われます。
クラスのコンストラクターは、
通常の配列やハッシュを使えば済む場面で、リファレンスを使うことはありません。

データを百科事典の記事の内容だとすれば、リファレンスはその記事が「何ページ目にあるか」に当たります。

スカラーのリファレンス

編集
#!/usr/bin/perl
use v5.30.0;
use warnings;

my $x = \42;
my $y = \$x;

print <<EOS;
\$x --> $x
ref \$x --> @{[ ref $x ]}
\${ \$x } --> ${ $x }
\$\$x --> $$x
\$y --> $y
\$\$y --> $$y
ref \$y --> @{[ ref $y ]}
ref \$\$y --> @{[ ref $$y ]}
\${ \${ \$y } } --> ${ ${ $y } }
\$\$\$y --> $$$y
EOS
実行結果
$x --> SCALAR(0x5588e7e33928)
ref $x --> SCALAR
${ $x } --> 42
$$x --> 42
$y --> REF(0x5588e7e339b8)
$$y --> SCALAR(0x5588e7e33928)
ref $y --> REF
ref $$y --> SCALAR
${ ${ $y } } --> 42 
$$$y --> 42

配列のリファレンス

編集
#!/usr/bin/perl
use v5.30.0;
use warnings;

my @x = ("A", "B", "C");
my $y = \@x;
print <<EOS;
\@x --> @x
\$y --> $y
\@\$y --> @$y

EOS

my $x = ["A", "B", "C"];
print <<EOS;
\$x --> $x
\@\$x --> @$x
\$x->[0] --> $x->[0]
\$x[0] --> $x[0]

EOS

$x = ["A", ["B"], "C"];
print <<EOS;
\$x --> $x
\@\$x --> @$x
\$x->[1]->[0] --> $x->[1]->[0]
\$x->[1][0] --> $x->[1][0]

EOS

$x = ["A"];
print <<EOS;
\$x --> $x
\@\$x --> @$x
\$x->[0] -->  $x->[0]
\${ \$x }[0] --> ${ $x }[0]
\$\$x[0] --> $$x[0]

EOS

$x = [["A"]];
print <<EOS;
\$x --> $x
\@\$x --> @$x
\$\@\$x --> $@$x
\$\@\$x[0] --> $@$x[0]
\$x->[0][0] -->  $x->[0][0]
\${ \$x }[0][0] --> ${ $x }[0][0]
\$\$x[0][0] --> $$x[0][0]
EOS
実行結果
@x --> A B C
$y --> ARRAY(0x55fade5819b8)
@$y --> A B C

$x --> ARRAY(0x55fade581358)
@$x --> A B C
$x->[0] --> A
$x[0] --> A

$x --> ARRAY(0x55fade5814a8)
@$x --> A ARRAY(0x55fade581430) C
$x->[1]->[0] --> B
$x->[1][0] --> B

$x --> ARRAY(0x55fade5814d8)
@$x --> A
$x->[0] -->  A
${ $x }[0] --> A
$$x[0] --> A

$x --> ARRAY(0x55fade581430)
@$x --> ARRAY(0x55fade581358)
$@$x --> ARRAY(0x55fade581430)
$@$x[0] --> A
$x->[0][0] -->  A
${ $x }[0][0] --> A 
$$x[0][0] --> A
$x->[0]$x[0]と略記できます(リファレンス経由の配列参照という意図がなくなるので、残すべきとの主張もあります)。

ハッシュのリファレンス

編集

ハッシュのリファレンスを生成するには、次のようにします。

#!/usr/bin/perl
use v5.30.0;
use warnings;

my %x = ( a => "Apple", b => "Banana", c => "Cherry" );
my $y = \%x;
print <<EOS;
\%\$y --> @{[ %$y ]}
\$y->{"a"} --> $y->{"a"}
\$y->{c} --> $y->{c}

EOS

my $x = { a => { e => "Apple", t => "Apricot" }, b => "Banana", c => "Cherry" };
print <<EOS;
\$x --> @{[ %$x ]}
\%\$x --> @{[ %$x ]}
keys \%\$x --> @{[ keys %$x ]}
values \%\$x --> @{[ values %$x ]}
\$x->{"a"} --> $x->{"a"}
\$x->{a} --> $x->{a}
\$x{a} --> $x{a}
\$x->{a}->{t} --> $x->{a}->{t}
\$x->{a}{t} --> $x->{a}{t}
EOS
実行結果
%$y --> a Apple c Cherry b Banana
$y->{"a"} --> Apple
$y->{c} --> Cherry

$x --> a HASH(0x55e8aa9af358) b Banana c Cherry
%$x --> a HASH(0x55e8aa9af358) b Banana c Cherry
keys %$x --> a b c
values %$x --> HASH(0x55e8aa9af358) Banana Cherry
$x->{"a"} --> HASH(0x55e8aa9af358)
$x->{a} --> HASH(0x55e8aa9af358)
$x{a} --> Apple
$x->{a}->{t} --> Apricot
$x->{a}{t} --> Apricot
$x->{a}->{t}$x->{a}と書くことが出来ますが、$x->{a}$x{a}と書くとHashをListに展開され意図した結果になりません。

サブルーチンへのリファレンス

編集

サブルーチンへのリファレンスを生成するには、次のようにします。

サブルーチンへのリファレンスの生成と呼出し
#!/usr/bin/env perl
use v5.20.0;
use warnings;

sub say_hello {
  say 'Hello'
}
my $ref = \&say_hello;

$ref->();
&$ref;
実行結果
Hello
Hello
サブルーチンへのリファレンスを得る場合、サブルーチン名の前に \& を付けます。
サブルーチンへのリファレンスからサブルーチンを呼出すときは、
$ref->();
->演算子で参照し、通常のサブルーチンと同じく実引数は()の中に書き渡します。
&$ref;
&を前置すれば->を省略でき、引数がなければ () も省略できます。

また、次のように書くことで、サブルーチンへのリファレンスを直接書くこともできます(クロージャや、無名サブルーチンとも呼ばれます)

my $ref = sub { ... };

サブルーチンへのリファレンスには特別な性質があり、サブルーチンへのリファレンスを生成した時の環境を保存します。

クロージャー
#!/usr/bin/env perl
use v5.20.0;
use warnings;

sub print_hello_name {
  my $name = shift;
  return sub { say "Hello $name!!" };
}
my $ref1 = print_hello_name('Kenta');
my $ref2 = print_hello_name('Taro');
$ref1->();
&$ref2;
say ref $ref1;
say ref $ref2;
実行結果
Hello Kenta!!
Hello Taro!!
CODE 
CODE
サブルーチンの中で参照される変数の値は、サブルーチンが定義されたものが参照されます。
組込み関数 ref を使うと、リファレンスの参照先のデータ型を表示できます。

正規表現のリファレンス

編集

他のリファレンス生成と少しやり方が異なりますが、正規表現のリファレンスを生成することも可能です。 正規表現のリファレンスを作るには、正規表現の前にqrを付けます。

my $regex = qr/\.pm$/; # 文字の最後に「.pm」が付いている文字にマッチする正規表現のリファレンスを生成

正規表現のリファレンスを使うには、次のようにします:

#!/usr/bin/perl
use v5.30.0;
use warnings;

# 引数の最後に「.pm」が付いているかどうかを調べるサブルーチン
#!/usr/bin/perl
use v5.30.0;
use warnings;

# 引数の最後に「.pm」が付いているかどうかを調べるサブルーチン
sub match_pm {
  my $name = shift;
  my $regex = qr/\.pm$/;
  if ($name =~ /$regex/) {
    say "$name -- match"
  } else {
    say "$name -- mismatch"
  }
}
match_pm('test.pm');
match_pm('test.pl');
match_pm('testpm');

my $re = qr/\.pm$/;
say ref $re;
say $$re
実行結果
test.pm -- match
test.pl -- mismatch
testpm -- mismatch
Regexp
(?^u:\.pm$)
置換は、s/$regex/置換後の文字/g としますが、この場合はリファレンス私にしないと結果を呼出し元に反映できません。

オブジェクト指向におけるリファレンス

編集
このページ「Perl/リファレンス」は、まだ書きかけです。加筆・訂正など、協力いただける皆様の編集を心からお待ちしております。また、ご意見などがありましたら、お気軽にトークページへどうぞ。