続・はじめてのPerl 5章

今日もまとめ。

5.1 データへの複数のリファレンス
Perlは参照カウンタによって、データにアクセスできるルート数を管理
    配列の作成で、まず1カウント
        それに対するリファレンスを作るたびに1カウントされる
            参照カウンタが0にならない限り、配列をメモリ内に残す

        サブルーチンに対してリファレンスを渡すと...
            @_にコピーされたタイミングで参照カウントが増える
                サブルーチンから戻る際に、
                コピーされたリファレンスは破棄され、参照カウントが減る

リファレンスが破棄されるケース
    ・変数がスコープから外れる時
    ・現在指しているデータ以外のデータを格納した時
        例えばundefの代入

参照カウンタが0になるとPerlは、別のデータのために使える状態にする
    メモリをOSに返すわけではない
my @array       = qw/ 1 2 3 /;  # 参照カウント1
my $first_ref   = \@array;      # 参照カウント2
my $second_ref  = \@array;      # 参照カウント3
my $third_ref   = $first_ref;   # 参照カウント4

$third_ref = undef; # リファレンスが破棄され、参照カウントは3に

{
    $ref = \@array; # 参照カウント4
}
# $refがスコープから外れたので、参照カウントは3に
5.2 無名配列
一般的に、変数のリファレンスは、変数より先に消滅する
    変数が先に消滅すると、変数の名前は消滅する
        しかし、リファレンス残っているならデータは消えない
            変数名はデータにアクセスする方法の1つにすぎない
my $ref = ();

{
    my @array = qw/ a b c /;    # 参照カウント1
    $ref = \@array;             # 参照カウント2
}
# スコープを抜けるので、@arrayは破棄。参照カウントは1になる

print "@$ref\n";    # a b c # 参照できる!
上記のコードでは@arrayはスコープからはずれ、名前が消滅する
    データは$refを指しているので消えない
        このとき、データが格納されている配列は名前がない
            このようは配列を「無名配列」と呼ぶ

$refが指しているデータにはデリファレンスでアクセス可能
    無名配列は、Perlの他の配列と機能を持つ配列
        要素の追加や、要素へのアクセスができる

    無名配列への参照カウントを増やすことは可能
        $refをコピーするだけ

    無名配列を指している最後のリファレンスが破棄されるまで、アクセス可能
$copy_of_ref = $ref;    # リファレンスをコピー。参照カウンタを増が増える

$ref = undef;           # 参照カウンタ1に
$copy_of_ref = undef;   # 参照カウンタ0に。データが破棄され、メモリは再利用待ちになる
5.3 参照カウンタとネストしたデータ構造
データは、最後のリファレンスが破棄されるまで残る
    これは、リファレンスがより大きいアクティブなデータ構造の中に含まれている場合も同じ
        例えば、以下のようなコード...
sub get_array
{
    my @abc = qw/ a b c /;
    my @abc_with_name = ( 'abc', \@abc );

    my @def = qw/ d e f /;
    my @def_with_name = ( 'def', \@def );

    return (
        \@abc_with_name,
        \@def_with_name,
    );
}
my @array = get_array();
上記の例では...
    サブルーチン内で定義した変数はサブルーチンから制御が帰ると破棄される
        だが、リファレンスが作られているので、データは残り続ける

    この状態で、@arrayを破棄すると...
        ドミノ倒し的に、参照カウンタが減らされ、参照カウンタが0になったデータは破棄される
            1. @abc_with_nameが指していたデータへの参照カウンタが減る
            2. 参照カウンタが0になるので、@abc_with_nameが保持していたデータは破棄される
            3. @abc_with_nameは、
               @abcが保持していたデータへのリファレンスを持つのでこれも破棄する
            4. @abcが指していたデータへの参照カウンタが減る
            5. 参照カウンタが0になるので、@abcがしていたデータは破棄される

    ネストされているデータのリファレンスを別に確保しておくと、
    @arrayを破棄してもデータは残る
        つまり、どこかでデータへのリファレンスが残っている場合、データも残る
my $ref = $array[0][1]; # ネストされているデータへのリファレンスをコピー

@array = ();            # @arrayのデータを上書き

print "@$ref\n";        # a b c # 参照できる!
5.4 参照カウントが問題を起こすとき
参照カウンタ
    メモリ管理の方法として、長い歴史がある
        欠点も明らかになっている
            データ構造が、循環的に相互参照しているとき問題がおきる
                例えば、以下のコードのように、
                データ構造がお互いのリファレンスを格納している場合
my @data1 = qw/ a b c /;
my @data2 = qw/ 1 2 /;

# お互いのリファレンスを格納
push @data1, \@data2;
push @data2, \@data1;
上記のコードでは...
    @data1が格納するデータに@data1又は、 @{$data2[2]}でアクセスできる
    @data2が格納するデータに@data2又は、 @{$data1[3]}でアクセスできる
        このような状態を循環参照という

循環参照となっている状態では、$data1[0]には以下のように色々なアクセス方法がある
# @data1の1つ目の値への色々なアクセス方法
print $data2[2][0], "\n";
print $data2[2][3][2][0], "\n";
print $data2[2][3][2][3][2][3][2][3][2][0], "\n";
@data1 @data2 がスコープから外れる前は
    @data1への参照カウンタは2となっている
        1. @data1自身
        2. @data2へ格納されているリファレンス

    @data2に関しても同じように、参照カウンタは2となっている
        1. @data2自身
        2. @data1へ格納されているリファレンス

@data1 @data2 がスコープから外れると
    @data1 @data2 という自身を指す配列名は破棄される
        だが、それぞれのデータに格納されているリファレンスはそのまま
            結果、参照カウンタは0にならず、データは破棄されない
                @data1 @data2 以外にデータに対してアクセスする手段はない
                    つまり、リファレンスを破棄できない
                        メモリリークが発生!

循環参照は、メモリ管理や、不連続メモリブロックの結合などで使われる
    Perlでは、このようなことは言語側で行ってくれる
        よって、Perlプログラマが循環参照を作ることはまずないはず
            ただし、他の言語で書かれたプログラムを移植する場合はそうとは限らない
                元のプログラムで、なぜ循環参照が使われているかを考え、
                Perlに最適化したアルゴリズムに書き換えるべき

将来のPerlでは、参照カウンタの変わりにガベージコレクションを使うことになる
    →あとで調べる

5.5 無名配列を直接作るには
配列のリファレンスを取るためだけに、配列を定義するのは面倒
    複雑なデータ構造の場合、面倒さはより顕著に
        配列名が重複しないように配列を定義する必要がある
            以下のコードのように、スコープを限れば配列名の重複を意識せずにすむ
my @array = ();
{
    my @temp = qw/ 1 2 3 /;
    push @array, \@temp;
}
{
    my @temp = qw/ a b c /;
    push @array, \@temp;
}
名前定義の面倒さは解決された
    しかし、配列のリファレンスを取るためだけに都度このように書くのはやはり面倒
        無名配列コンストラクタを使えば解決できる!
            これは、ブラケット([])のもう一つの使い方
my @array = ();
push @array, [ qw/ 1 2 3 / ];   # 無名配列コンストラクタ
push @array, [ qw/ a b c / ];   # 無名配列コンストラクタ
無名配列コンストラクタ
    ブラケットで囲まれた値で、
    初期化された新しい無名配列を作り、その配列へのリファレンスを返す

    これを使うことにより...
        ・一時配列名を考える必要ない
        ・コードの簡略化

上記のコードをさらに簡略化すると以下のようになる
my @array = (
    [ qw/ 1 2 3 / ],
    [ qw/ a b c / ],
);
5.6 無名ハッシュ
無名配列と同様に、無名ハッシュも作れる
    無名ハッシュコンストラクタを使う

無名ハッシュコンストラクタ
    ブレース({})のもう一つの使い方

    ブレースで囲まれた部分を評価し、
    キーと値のペアから無名ハッシュを構築、そのハッシュのリファレンスを返す

    ブロックと無名コンストラクタは構文木のほぼ同じ位置でブラケットを使う
        コンパイラがブレースの意図を判断しにくい場合ヒントを与える必要がある
            ・無名ハッシュコンストラクタの場合
                { の前に + をつける
                    $hash_ref = +{ };
                
            ・ブロックの場合
                ブロックの冒頭に;をつける
                    これは、空の文を示す
                        {; ... }
my $ref = ();

# 一時ハッシュを作って、ハッシュリファレンスを取得する
my %hash = (
    a   => 1,
    b   => 2,
);
$ref = \%hash;

# 無名ハッシュコンストラクタを使い、ハッシュリファレンスを取得する
$ref = {
    a   => 1,
    b   => 2,
};
5.7 自動生成
存在しない変数や、undef値がセットされている変数をデリファレンスすると、
Perlは自動的に適切な空アイテムへのリファレンスを作る
    この処理を自動生成と呼ぶ
        自動生成によって、処理を続けることができる
my $ref = ();

# 空配列リファレンスが自動生成され、それをデリファレンスした配列へ値が格納される
@$ref = ( 1, 2, 3 );

# 以下のコードと同義
# $ref = [];
# @$ref = ( 1, 2, 3 );
5.8 自動生成とハッシュ
自動生成は、ハッシュリファレンスでも働く
    undefが格納されている変数をハッシュリファレンスであるかのようにデリファレンスすると、
    空の無名ハッシュが作られ、そのリファレンスが変数にセットされる
my $ref = ();

$ref->{abc}{cdf} = 1;
# これは以下のコードと同義
# $ref = {};
# $ref->{abc} = undef;
# $ref->{abc} = {};
# $ref->{abc}{cdf} = 1;

感想

[] が無名配列コンストラクタ、{} が無名ハッシュコンストラクタ と呼ばれることを知ることができた。

Perlがどのように自動生成を処理しているのか知れた事も嬉しい。


続・初めてのPerl 改訂版

続・初めてのPerl 改訂版