大きなプログラムを組む(2)
2021/05/01
リストやハッシュへのデータ追加
  • 今回から参照を使ったデータの構造化について説明しますが、その前に構造化データの要素となるPerlのリストとハッシュについて見ていきます。

  • C言語等のプログラム経験がある場合、リストやハッシュにデータを追加したいと考えると「メモリ確保」や「メモリ解放」という言葉が頭に浮かぶかもしれませんが、Perlはメモリ確保/解放を自動で行ってくれます

  • そのためリストやハッシュのデータ追加は、メモリ確保を意識せず下記のように簡単にできます。
    my @list = (); # 最初は空のリスト
        $list[0]='00'; # 要素[0]追加
        $list[1]='10'; # 要素[1]追加
        $list[1]='20'; # 要素[2]追加
        
    my %hash = (); # 最初は空のハッシュ
        $hash{'Tokyo'}='Shinjyuku';  # キー Tokyo 追加
        $hash{'Hokkaido'}='Sapporo'; # キー Hokkaido 追加
        $hash{'Ibaraki'}='Mito';     # キー Ibaraki 追加
    

参照使用例の一つ: 関数間のデータ受け渡し
  • 次に関数間で複数のリストやハッシュを渡すケースについて考えてみます。先程作成した、@list と %hash を関数に渡してみましょう。
    { # ---- main closure ----
        my @list = (); # 空リスト
            ...略
        my %hash = (); # 空ハッシュ
            ...略
        
        show_data(@list, %hash);        # @listと%hashをshow_data()に渡す
    }
    
    sub show_data {
        my (@list, %hash) = @_;         # @listと%hashを受ける(つもり)
        
        foreach my $val (@list) {       # @listの値を表示
          print "list: ",$val,"\n";
        }
        foreach my $key (keys(%hash)) { # %hashの値を表示
          print "hash: ",$key,'=>',$hash{$key},"\n";
        }
    }
    

  • 実行結果は下記のようになります。@listと%hashが連結されて一つのリスト扱い(*1)になってしまいます。
    list: 00
    list: 10
    list: 20
    list: Hokkaido  <--+--- あれ? %hash の key/value ペアがリスト扱いになった?
    list: Sapporo   <--+
    list: Ibaraki   <--+
    list: Mito      <--+
    list: Tokyo     <--+
    list: Shinjyuku <--+
                    <------ %hash は存在しないことになっている
    

  • こんなときは参照(リファレンス/Reference)を使えば、@listと%hashを別の情報として渡すことができます。
        ...略
        show_data(\@list, \%hash); # \マークを付けて参照を渡す
    }
    
    sub show_data {
        my ($list, $hash) = @_;                        # 2個のスカラー。
                                                       # それぞれ@listと%hashのリファレンスを受ける
        foreach my $val (@{$list}) {                   # @{$list}はリストのデリファレンス
          print "list: ",$val,"\n";
        }
        foreach my $key (keys(%{$hash})) {             # %{$hash}はハッシュのデリファレンス
          print "hash: ",$key,'=>',$hash->{$key},"\n"; # リファレンス経由アクセス
        }
    }
    
    
    [実行結果] list: 00 list: 10 list: 20 hash: Ibaraki=>Mito <--+--- リストとハッシュを別データとして渡すことができた hash: Hokkaido=>Sapporo <--+ hash: Tokyo=>Shinjyuku <--+

そもそも参照(リファレンス/Reference)とは何か
  • さて、何故参照(リファレンス)を使うと複数のリストやハッシュが連結されることなく関数に渡されるのでしょうか。ここで参照(リファレンス)とは何かを考えてみましょう。

  • 最初はリファレンスを使用せず関数 show_data() の引数に @list と %hash をそのまま指定した場合ですが、このときは %hash もリストタイプのデータと解釈され、@list と %hash が一つのリストに連結された状態で渡されます。

  •  
    Figure 1: show_data()の引数に@listと%hashを指定

  • またこの時、関数 show_data() 内で構成されたリストは、main closure内の @list, %hash とは別のメモリ領域に値をコピーされたオブジェクト(別のオブジェクト)になります。そのため関数 show_data() 内のリストデータを操作しても、main closure 側の @list, %hash に変化は出ません。これは一般的に「値渡し(Call by value)」と呼ばれます。

  • 次はリファレンスを使用した場合です。リファレンスはデータ/オブジェクトの置き場所を表します(*2)。置き場所を参照/経由してデータにアクセスします。show_data()の引数に \@list と \%hash のように"\"マーク(*3)を付けてリファレンスを渡しています。

  •  
    Figure 2: show_data()の引数に\@listと\%hashを指定

  • 関数 show_data() は2個のスカラー値($list, $hash)を受けます。そのスカラーをリスト(@)とハッシュ(%)のリファレンスとして扱うことで参照元データにアクセスします。これがPerlの「参照渡し(Call by reference)」です。

  • リファレンスを経由したデータの操作は、当然ながら実体である参照元にも反映されます。もし独立したデータとしたい場合は、デリファレンスして値をコピーすればOKです。
      my @list_local = @{$list};
      my %hash_local = %{$hash};

今回のまとめ
  • さて今回はリファレンスに触れたレベルで終わってしまいましたが、項目としては以下の二点でした。
    • Perlのリスト/ハッシュは自動的に動的確保/解放が行われる
    • Perlのリファレンスはデータの置き場所を指す

  • 1番目の「動的確保/解放」はリファレンスと関係してる? と言われそうですが、はい。関係しています。リファレンスはPerlの中核要素で特に「解放」に関係しています。まだまだ説明必要なため、次回も引き続きリファレンスを扱います
Notes
  • Perlにおいて、ハッシュはリストの上位互換です。
  • C/C++で言うところのポインタです。C++にも参照はありますが、C++の参照は別名(リンク)扱いなので、ポインタの方が似ています。
  • 使用しているフォントによって表示がバックスラッシュ(\)になったり、円マーク(¥)になったりしますが、その点はご了承ください。
Copyright(C) 2021 Altmo
本HPについて