Perl の変数についてもう一度まとめておきます。主な変数の種類には スカラー変数、配列、連想配列 があります。
スカラー変数は数値や文字列を保持することができます。
$xx = 123; # スカラー(数値スカラー) $xx = "Japan"; # スカラー(文字列スカラー)
配列は、数値をキー(添字)とした、スカラー値の集合です。
$xx[0] = "AAA"; # 配列(リスト) $xx[1] = "BBB"; # 配列(リスト) $xx[2] = "CCC"; # 配列(リスト)
次のように記述することもできます。
@xx = ( "AAA", "BBB", "CCC" ); # 配列(リスト)
連想配列は、文字列をキーとした、スカラー値の集合です。
$xx{'NAME'} = "Tanaka"; # 連想配列(ハッシュ) $xx{'AGE'} = 26; # 連想配列(ハッシュ)
次のように記述することもできます。
%xx = ( NAME => "Tanaka", AGE => 26 ); # 連想配列(ハッシュ)
$xx と @xx と %xx はまったく別物です。$xx の値を変更しても、@xx や %xx に影響を与えることはありません。下記の例では、@xx に ( "XYZ" ) を代入しても、$xx は "ABC" のままです。
$xx = "ABC"; @xx = ( "XYZ" ); print $xx; # ABC と表示される。
print() では、「書き出す値を省略すると省略時変数 $_ の値を書き出す」と定義されています。print() の他にも多くの機能で、$_ は省略時の対象となる変数として使用されます。例えば、下記のそれぞれの 2行は同じ意味を持ちます。
# print における $_ の省略 print $_; print; #における $_ の省略 while ($_ = <IN>) { ... } while (<IN>) { ... } # chop() における $_ の省略 chop($_); chop(); # 正規表現マッチングにおける $_ の省略 if ($_ =~ /To:/) { ... } if (/To:/) { ... }
次のようなスクリプトを考えてみましょう。
open(IN, "data.txt"); while ($line = <IN>) { chop($line); if ($line =~ /^From:/) { print $line; } } close(IN);
省略時変数 $_ を用いて、これを次のように記述することができます。
open(IN, "data.txt"); while (<IN>) { chop; if (/^From:/) { print; } } close(IN);
省略は Perl の美学なのだそうです。しかし、初心者プログラマーにも分かりやすいプログラミングや、省略時変数 $_ の上書きによるバグの埋め込みにも気を配りましょう。
環境変数 とは、親プロセスから子プロセスに受け継がれる変数です。よく使用される環境変数には PATH(パスリスト)や TZ(タイムゾーン)などがあります。例えば CGI の実行においても、親プロセス(Webサーバー)が、CGIスクリプト(子プロセス)に対してブラウザからの情報を伝えるために使用されています。
環境変数を参照するには特殊変数 $ENV{...} の値を参照します。例えば環境変数 PATH の値を参照するには次のようにします。
print $ENV{'PATH'}; # 環境変数 PATH の値を表示する
例えば、環境変数 TZ に値を設定するには、次のようにします。
$ENV{'TZ'} = "JST-9"; # 環境変数 TZ に JST-9 を設定する
環境変数 | 説明 |
---|---|
PATH | パス名を省略してコマンドを実行した場合に、コマンドを検索する対象となるフォルダの一覧。 |
TZ | タイムゾーン。例えば日本の場合、国名を示す JST と、標準時からの時差を示す -9 から、JST-9 となる。 |
TEMP | テンポラリファイルを作成する際によく使用されるフォルダ。 |
TMP | TEMP とほぼ同義。OS やアプリケーションによって異なる。 |
その他、CGI でよく使用される環境変数については「CGIで参照可能な環境変数一覧」を参照してください。
型グロブ *xx は、$xx や @xx や %xx などの変数の総称のようなものです。例えば、変数 $xx、@xx、%xx が存在するとき、*xx を用いることにより、これらの変数への参照をサブルーチンにまとめて渡すことができます。
下記の例ではサブルーチンは、型グロブ *xx を *zz として受け取ります。これにより $zz は $xx、@zz は @xx、%zz は %xx を示すようになります。
$xx = 123; # スカラー @xx = ( "AAA", "BBB", "CCC" ); # 配列 %xx = ( NAME => "TANAKA", AGE => 26 ); # 連想配列 func(*xx); sub func { local(*zz) = @_; # スカラーとして表示 print "$zz\n"; # 配列として表示 foreach $value (@zz) { print "$value "; } print "\n"; # 連想配列として表示 while (($key, $value) = each(%zz)) { print "$key=$value, "; } print "\n"; }
実行結果は次のようになります。
123 AAA BBB CCC NAME=TANAKE, AGE=26,
型グロブを用いて実現するテクニックには以下のようなものがあります。
Perl には定数の概念が無いので、通常は定数も変数として定義します。
$PI = 3.141592653589793;
これは次のように使用します。
$hankei = 2.0; $menseki = $hankei * $hankei * $PI;
ただし、$PI の値は簡単に変更するとこができるので、定数とは言えません。
$PI = が、サブルーチンとして定義することにより、変更不可能な定数を表現することができます。下記では、3.14... という値を返すサブルーチン PI と、2.71... という値を返すサブルーチン E を定義しています。
sub PI { 3.141592653589793; } sub E { 2.718281828459045; }
次のように使用することができます。
$hankei = 2.0; $menseki = $hankei * $hankei * PI;
値を参照する度にサブルーチンを呼び出すと処理が遅くなるので、サブルーチンに () をつけます。こうすると、サブルーチンは インラインサブルーチン として定義されます。インラインサブルーチンを使用すると、必要メモリは多少増えますが、処理は速くなります。インラインサブルーチンは、そのサブルーチンを利用する箇所よりも前に記述してください。
sub PI () { 3.141592653589793; } sub E () { 2.718281828459045; }
$menseki = $hankei * $hankei * PI; の計算を通常のサブルーチンとインラインサブルーチンで計測したところ、インラインサブルーチンの方が倍くらい早くなりました。
スカラー変数に対して undef() を実行すると、その変数が削除され、メモリが開放されます。
undef($xx);
配列の要素に対して undef() を実行した場合、その要素に「未定義値」という特別な値が代入されます。配列の個数は変わりません。要素の削除には delete() を用いてください。
@xx = ( "AAA", "BBB", "CCC" ); undef($xx[1]); for ($i = 0; $i <= $#xx; $i++) { print "xx[$i] = $xx[$i]\n"; }
結果は次のようになります。
xx[0] = AAA xx[1] = xx[2] = CCC
連想配列の場合も同様です。「未定義値」という特別な値を持つ要素となります。
%xx = ( "AAA" => "aaa", "BBB" => "bbb", "CCC" => "ccc" ); undef($xx{'BBB'}); while (($name, $value) = each(%xx)) { print "$name = $value\n"; }
結果は次のようになります。
CCC = ccc BBB = AAA = aaa
@xx や %xx に対して undef を行うと、配列や連想配列内のすべての要素を削除してメモリを開放します。
undef(@xx); undef(%xx);
delete() は連想配列の要素を削除してメモリを開放します。スカラー変数や、配列変数や、連想配列全体を削除するには undef() を用いてください。perl v5.6 では配列要素に対しても delete の使用が可能になりました。$xx[2] を削除しても $xx[3] が $xx[2] にシフトすることはありません。この場合の削除には splice() を用いてください。配列の最後の要素が削除された場合は、$#配列名 の値も変動します。
# delete($xx); 使用不可 # delete(@xx); 使用不可 # delete(%xx); 使用不可 delete($xx[0]); delete($xx{'AAA'});
undef された要素は defined() では偽になりますが、exists() では真になります。delete された要素は、defined() でも exists() でも偽になります。
reset() は、スカラー値、配列、連想配列に関わらず、指定した変数をすべて削除します。"x" は x で始まるすべての変数、"ab" は a または b ではじまる全ての変数、"a-z" は小文字で始まるすべての変数を削除します。
reset "x"; # 変数 x* をリセット reset "ab"; # 変数 a*、b* をリセット reset "a-z"; # 変数 a* ~ z* をリセット
reset "A-Z" とすると、@INC や %ENV まで削除してしまうので注意が必要です。とてもマニアックなスクリプトを書くとき以外、この機能を利用することはないでしょう。
defined() は、変数が定義されており、かつ、「未定義値」以外の値をもっているかどうかを調べます。
if (defined($xx)) { print "$xx\n"; } if (defined($xx[0])) { print "$xx[0]\n"; } if (defined($xx{'AAA'})) { print "$xx{'AAA'}\n"; } if (defined(@xx)) { print "\@xx\n"; } if (defined(%xx)) { print "\%xx\n"; }
exists() は、連想配列の要素が存在するかどうかを調べます。たとえ、要素が undef されていても、要素としては存在するので、exists は真を返します。perl v5.6 以降では配列にも対応しています。
# if (exists($xx)) 使用不可 # if (exists(@xx)) 使用不可 # if (exists(%xx)) 使用不可 if (exists($xx[0])) { print "$xx[0]\n"; } if (exists($xx{'AAA'})) { print "$xx{'AAA'}\n"; }
配列や連想配列の要素には、「存在しているけど定義されていない」という状態があります。下記の場合、$xx[0] は存在していて値もある、$xx[1] は存在しているけど未定義の状態、$xx[2] は存在していない定義もされていないという状態になります。
%xx = ( "AAA" => "aaa", "BBB" => "bbb", "CCC" => "ccc"); undef($xx{'AAA'}); delete($xx{'BBB'}); foreach $key ("AAA", "BBB", "CCC") { if (exists($xx{$key})) { print "xx{$key} exists\n"; } if (defined($xx{$key})) { print "xx{$key} defined\n"; } }
これを実行すると次のようになります。
xx{AAA} exists xx{CCC} defined xx{CCC} exists
$name の値が "To" の時、${$name} は $To と同じ意味を持ちます。この仕組みを利用して、変数名をダイナミックに生成することができます。
$name = "To"; ${$name} = 'aaa@xxx.yyy.zzz'; # $To = '...' と同じ意味 print "$To\n";
混乱が無ければ、${$name} は $$name と記述することもできます。
$$name = 'aaa@xxx.yyy.zzz'; # $To = '...' と同じ意味
例えば、次のような内容のファイル(data.txt)があるとします。
NAME=Tanaka AGE=26 ADDRESS=Tokyo
これを次のようなプログラムで読みこむことにより、各行の値を $NAME、$AGE、$ADDRESS で参照できるようになります。
open(IN, "data.txt"); while ($line = <IN>) { $line =~ s/[\r\n]*$//; # 改行コードを削除 $line =~ /([^=]*)=(.*)/; # 「名前=値」に分解 if ($1) { ${$1} = $2; } # $名前 = 値 } close(IN); print "NAME = $NAME\n"; print "AGE = $AGE\n"; print "ADDRESS = $ADDRESS\n";
実行結果は次のようになります。
NAME = Tanaka AGE = 26 ADDRESS = Tokyo
リファレンスは Perl 5 で新しく実装された型です。C言語のポインタに相当します。ある変数のリファレンスを得るには \ を用います。
$name = "Tanaka"; $ref = \$name;
$ref を表示すると例えば、SCALAR(0x17652cc) のように表示されます。これは、$name というスカラーの値が、メモリの 0x17652cc 番地に SCALAR 型で格納されていることを示します。
print "$ref\n"; # SCALAR(0x17652cc) と表示される
リファレンスから元の値を得るには $$変数名 を用います。これを デリファレンス と呼びます。
print "$$ref\n"; # Tanaka と表示される
ref() は、引数が参照値であれば、その元の型によって SCALAR、ARRY、HASH、CODE、REF、GLOB、LVALUE などの 型名(→型のいろいろ)を返します。
print ref($ref) . "\n"; # SCALAR と表示される
リファレンスを用いて実現されるテクニックには次のようなものがあります。
$p = [ ]; や、$p = { }; は、無名配列 や 無名連想配列 へのリファレンス(無名参照)を生成します。無名参照は、それを参照する変数がひとつ以上ある間存在し、無くなると自動消滅します。
$p = { }; $p->{NAME} = "Tanaka";
無名参照を用いてたとえば、下記のような表形式のファイルを読み込むことができます。
Tanaka 26 Tokyo Suzuki 32 Osaka Yamada 18 Nagoya
スクリプトは次のようになります。
open(IN, "data.txt"); while (<IN>) { s/[\r\n]*$//; # 改行を削除 ($name, $age, $addr) = split(/\s+/); # ホワイトスペースで分割 $p = {}; # 無名参照オブジェクトを生成 $p->{NAME} = $name; # 名前(NAME)を設定 $p->{AGE} = $age; # 年(AGE)を設定 $p->{ADDR} = $addr; # 住所(ADDR)を設定 push(@persons, $p); # 無名参照オブジェクトを配列に加える } close(IN); foreach $p (@persons) { print "NAME=$p->{NAME}, AGE=$p->{AGE}, ADDR=$p->{ADDR}\n"; }
実行結果は次のようになります。
NAME=Tanaka, AGE=26, ADDR=Tokyo NAME=Suzuki, AGE=32, ADDR=Osaka NAME=Yamada, AGE=18, ADDR=Nagoya