読者です 読者をやめる 読者になる 読者になる

Perl - リファレンスの説明(2)

Perl 初心者が初心者なりにリファレンスを説明してみるの2回目。前回はリファレンスを取得することと、そのリファレンスから元のものにアクセスする(デリファレンスする)方法を書いた。今回は無名配列、無名ハッシュ、無名サブルーチン、無名スカラーについて。特に無名配列、無名ハッシュについて例を挙げて説明する。
前回同様、丸括弧()、ブラケット[]、ブレース{} の違いを意識しながら読んでほしい。

目次

  1. 復習
  2. 無名配列、無名ハッシュ、無名サブルーチン、無名スカラー

1.復習

変数からリファレンスをとるには前にバックスラッシュ(Windows は円マーク)を付ければよかった。

my $s = "This is a pen.";
my @a = ("Apple", "Orange", "Melon");
my %h = (jp => 'Japan', kr => 'South Korea');
sub f { print $_[0] }

my $s_ref = \$s;
my $a_ref = \@a;
my $h_ref = \%h;
my $f_ref = \&f;

リファレンスから元のものにアクセスする(デリファレンスする)には、スカラーは ${ }、配列は @{ }、ハッシュは %{ }、サブルーチンは &{ } を使って

${ $s_ref } # $s
@{ $a_ref } # @a
%{ $h_ref } # %h
&{ $f_ref } # &f

とすればよかった。またリファレンスから配列の要素、ハッシュの要素へのアクセスや、サブルーチンの引数の指定の仕方は、それぞれ ${ }[]、${ }{}、&{ }() を使って

${ $a_ref }[1]     # "Orange"
${ $h_ref }{kr}    # "South Korea"
&{ $f_ref }('hey') # hey とプリント

だった。

2.無名配列、無名ハッシュ、無名サブルーチン、無名スカラー

まず次の例を見て欲しい。

my @array = ('apple', 'orange', 'banana');
my $array_ref = \@array;

for my $item ( @{ $array_ref } ) {
    print $item, "\n";
}

特に新しいことはなく、まず配列 @array を作って、そのリファレンスを $array_ref に入れて、その $array_ref を通じて for 文で配列にアクセスしている。ここで配列 @array にはリファレンスを入れた変数 $array_ref を通じてアクセスできるんだから、わざわざ配列 @array を作るのは無駄に見える。 このようなときのために、Perl では配列をどこかに作ってそのリファレンスだけを表す記法 [] が用意されている。

つまり上のソースコードは次のようにも書ける。

# リストをブラケット [] でくくることによって、
# 配列をどこかに作ってそのリファレンスだけを表す。
my $array_ref = ['apple', 'orange', 'banana']; # $array_ref にリファレンスが入る

for my $item ( @{ $array_ref } ) {
    print $item, "\n";
}

('apple', 'orange', 'banana') には、そのリファレンスを通してのみアクセスできて配列としての名前はない。このような配列を名前がないから「無名配列」と呼ぶ。
無名ハッシュをつくってそのリファレンスを表す方法もある。リストをブレース {} でくくればいい。

# 普通のやり方
my %hash = (jp => 'Japan', kr => 'South Korea');
my $hash_ref = \%hash;
print ${ $hash_ref }{jp};

# どうせ $hash_ref を通じてアクセスするんだから、もとの %hash は必要ない。
# そこで、リストをブレース {} でくくり、
# どこかにハッシュを作ってリファレンスだけを表す。
my $hash_ref = {jp => 'Japan', kr => 'South Korea'};
print ${ $hash_ref }{jp};

無名サブルーチンは sub の後に名前をかかなければいい。

# 普通のやり方
sub say_hello_to {
    my $name = shift;
    print "Hello $name!\n";
}
my $sub_ref = \&say_hello_to;
&{ $sub_ref }('John'); # Hello John!

# 無名サブルーチンをつかって、わざわざ say_hello_to をつくらない方法
my $sub_ref = sub {
    my $name = shift;
    print "Hello $name!\n";
};
&{ $sub_ref }('John'); # Hello John!

いちおう無名スカラーも。

my $text_ref   = \'This is a pen.';
my $number_ref = \123;
print ${$text_ref}, ${ $number_ref }, "\n";

3.例

無名配列、無名ハッシュを使うのは、どうせリファレンスでアクセスできるなら、もとの配列、ハッシュに名前なんていらないでしょ、という理由からだ。いくつか例を書こう。

行列の例

前回行列を次のようにして作った。

my @first  = (10,20,30);
my @second = (40,50,60);
my @third  = (70,80,90);
my @matrix = (
    \@first,
    \@second,
    \@third
);
# アクセスの例
print ${ $matrix[0] }[2]; # 30

これどう見ても、はじめに名前付きの配列 @first, @second, @third をつくるのが無駄だ。どうせ以後は @matrix からしか要素にアクセスはしないだろう。そこで無名配列を使い次のようにする。

my @matrix = (
    [10, 20, 30],
    [40, 50, 60],
    [70, 80, 90]
);
print ${ $matrix[0] }[2]; # 30

無駄なところも減ったし、こっちの方が行列をつくってるということも分かり易い。
さて、行列は次のようにしてもいい。

my $matrix = [
    [10, 20, 30],
    [40, 50, 60],
    [70, 80, 90]
];
print ${ ${ $matrix }[0] }[2]; # 30

変えたところは、@matrix = ( .... ); と配列を使うのではなく、$matrix = [ .... ]; のようにして、ここにも無名配列をつかってリファレンスを $matrix に入れているところだ。第1行、第3列要素 30 にアクセスしているところを見てみると

${ ${ $matrix }[0] }[2]

のようになってる。分かりづらいと思うので順を追ってこれを理解してみる。
まず $matrix は配列のリファレンスが入っているのだから、もとの配列を得るには

@{ $matrix } # これで配列 ([10, 20, 30], [40, 50, 60], [70, 80, 90]) を表す

とすれば良かった。配列のなかでも、第1要素 [10, 20, 30] にアクセスしたいんだから、@{ } ではなく ${ }[] を使って

${ $matrix }[0] # [10, 20, 30] を表す

とする。ここからさらに第3要素 30 にアクセスしたい。[10, 20, 30] は配列 (10, 20, 30) のリファレンスを表すんだから元の配列を得るためには

@{  ${ $matrix }[0]  } # これで配列 (10, 20, 30) を表す

とすると良い。でも今は配列全体ではなく配列の第3要素にアクセスしたいから @{ } ではなく${ }[] を使って

${  ${ $matrix }[0]  }[2] # これで 30 を表す

とする。分かっただろうか?

ネストが深い例

もう一つ例を書く。これは無名ハッシュや無名ハッシュを駆使してすこし複雑なデータを表現したもの。

my $family = {
    family_name => 'Yamada',
    address     => 'Tokyo',
    parents     => [
        {
            name => 'Taro',
            age  => 30
        },
        {
            name => 'Hanako',
            age  => 32
        }
    ],
    children    => [
        {
            name => 'Ichiro',
            age  => 5
        },
        {
            name => 'Jiro',
            age  => 2
        }
    ]
};

# アクセスの例
print ${ $family }{family_name}, "\n";
print ${ ${ ${ $family }{parents} }[0] }{name}, "\n";
for my $child ( @{ ${ $family }{children} } ) {
    print ${ $child }{name}, ' ';
    print ${ $child }{age}, "\n";
}

# 実行結果
# Yamada
# Taro
# Ichiro 5
# Jiro 2

分かるだろうか? すこしずつ理解して欲しい。

おわりに

以上、無名配列、無名ハッシュなどを紹介し、それを使って簡潔に、少し複雑なデータを表現する方法を示してみた。これが分かれば、Perl が楽しくなる! 多分。

ただ、上記で $family の中の Taro にアクセスするところの

${ ${ ${ $family }{parents} }[0] }{name}

を見て、毎回こんな風に書かなきゃいけないのか、と思う人が多くいるだろう。実はこれもっと簡潔に

$family->{parents}[0]{name}

と書ける。この矢印記法については リファレンスの説明(3) に書く。

今日の疑問

  • 配列とリストがこんがらがってきたぞ。