まず、言葉を覚えましょう。my を使って定義された変数は、レキシカルスコープ変数 と呼びます。そして、my を使って変数を宣言することをレキシカルスコープ宣言と呼びます。
宣言の仕方は
などです。では、レキシカルスコープ変数の有効範囲は、どこまでかと言うと、次の例を見てください。my $var; #宣言のみ my $var = 'hello'; #宣言と同時に値も同時に格納 my($foo, $var); #複数の変数名を同時に宣言
1| $var = 'サブルーチンの外';
2| print $var; #「サブルーチンの外」が出力される
3| &example;
4| sub example {
5| my $var = 'サブルーチンの中';
6| print $var; #「サブルーチンの中」が出力される
7| }
8| print $var; #「サブルーチンの外」が出力される
この場合、 2 行目で出力される値は、「サブルーチンの外」ですね。そして 6 行目で出力される値は「サブルーチンの中」となります。サブルーチンの中で、$var のレキシカルスコープ宣言をしているため、新たにメモリー上に値が保持されることになります。もちろん、2 行目で定義した $var の値も保持されたままです。それぞれの $var は、同じ名前にもかかわらず、変数名も値も、別々に保持されます。つまり、それぞれの $var は、名前は同じですが、まったく別の変数として処理されます。ただし、5 行目のサブルーチン内で定義された $var の変数名と値は、サブルーチンを抜けた時点で、破棄されてしまいます。
では、8 行目では、何か出力されるのでしょうか。上の説明でお分かりかと思いますが、「サブルーチンの外」が出力されるわけです。サブルーチンの外からは、5 行目で定義した値は参照できないことに注意してください。
次に、5 行目で my を使って宣言しなかった場合には、どうなるでしょうか。
1| $var = 'サブルーチンの外';
2| print $var; #「サブルーチンの外」が出力される
3| &example;
4| sub example {
5| $var = 'サブルーチンの中';
6| print $var; #「サブルーチンの中」が出力される
7| }
8| print $var; #「サブルーチンの中」が出力される
上記の通り、8 行目の値が変わってしまいましたね。5 行目で my を使って局所化していなかったため、1 行目で代入した値が、5 行目で書き換わってしまったのです。そのため、8 行目の出力結果も変わってしまったというわけです。意図的にそうしたいのであれば問題ないのですが、そうでなかったら、バグになってしまいますね。
次に、サブルーチンがネスト(入れ子)になったパターンを紹介します。ここで言っているネストとは、サブルーチンの中でサブルーチンを呼び出した場合のことを指しています。
1| $var = 'サブルーチンの外';
2| &example;
3| sub example {
4| my $var = 'サブルーチンの中';
5| print $var; #「サブルーチンの中」が出力される
6| &example2;
7| }
8| sub example2 {
9| print $var; #「サブルーチンの外」が出力される
10| }
11| print $var; #「サブルーチンの外」が出力される
この例では、4 行目でレキシカルスコープ変数として $var を宣言していますので、この $var の有効範囲は、7 行目までとなります。しかし、その手前の 6 行目でサブルーチン example2 を呼び出していますね。その場合、4 行目で宣言された $var は example2 の中でも有効になるのでしょうか。答えは、No です。9 行目で呼び出している $var は、グローバル変数の $var で、1 行目で宣言されたものなんです。
話が若干それますが、もし、変数をサブルーチンに引き継ぎたい場合には、以下の例のように、引数をを正しく与えてるべきです。
1| $var = 'サブルーチンの外';
2| &example;
3| sub example {
4| my $var = 'サブルーチンの中';
5| print $var; #「サブルーチンの中」が出力される
6| &example2($var);
7| }
8| sub example2 {
9| my($var) = @_;
10| print $var; #「サブルーチンの中」が出力される
11| }
12| print $var; #「サブルーチンの外」が出力される