Perl/CGIプログラムのリファレンス

Perl/CGIプログラムでリファレンスを使う方法を解説します。

  1. 編集前記
  2. リファレンスとは?
  3. リファレンスの基礎
  4. リファレンスの応用
  5. 編集後記

編集前記

今回の編集前記は、特殊変数についてある程度まとめてみることにしましょう。

Perl/CGIプログラミングといえば、正規表現、パターンマッチに続くぐらい重要な用語それが特殊変数?

↑まぁ、何の根拠もないわけですが(笑)。

ある意味、そのぐらい大切かもしれないということです。

他人のプログラムをトレースする際、そこに書かれている特殊変数がわからなければ、かなりきついですからね。

というわけなので、ここでは特殊変数をまとめてみましょう!

とか言いながら実は、Perl/CGIプログラムには、本当にたくさんの特殊変数がありますから、すべて紹介するわけにはいきませんが…。

紹介したら最後、それだけでこのページ終わってしまいますし(苦笑)。

なのでいつものように、独断と偏見により、よく使用するであろう特殊変数を紹介していくことにします。

1、「$_」

まずは、泣く子も黙る「$_」からいきましょう。

このPerl/CGIプログラミング学習にもよく出てきますよね。

「$_」は、デフォルト変数として使用されます。

#!/usr/bin/perl

$_ = 'Perl/CGI';

print "Content-type: text/html\n\n";
print;
exit;

上記のように例えば、「$_」に文字列を入れておいて、「print;」とすれば、「$_」内を出力してくれます。

さらに、 パターンマッチ を書けば、「$_」を割り当ててくれます。

2、「@_」

流れ的に考えると次は、「@_」ですよね。

「@_」は、 サブルーチンの引数 を格納していますから、サブルーチン内で引数を受け取るときに使用します。

#!/usr/bin/perl

use strict;

my $string = 'Perl/CGI';
&view($string);

sub view {
my ($view) = @_;

print "Content-type: text/html\n\n";
print $view;
exit;
}

「配列状態にした変数 = @_;」などのようにして引数をまとめて受け取ります。

頭から順に受け取る場合は、「単純変数 = shift;」をくりかえします。
末尾から受け取る場合は、「単純変数 = pop;」をくりかえします。
順番を無視してピンポイントで受け取りたいときには、「単純変数 = $_[添え字]」とします。

3、「$0」

「$0」は、現在実行しているPerl/CGIプログラムファイル名を取得します。

#!/usr/bin/perl

print "Content-type: text/html\n\n";
print $0;
exit;

上記の場合だと例えば、「index.cgi」とかが表示されます。

でも、動作環境によってはうまくいかない場合もあるので注意しましょう。

いろいろな環境で動作させる予定がある場合には、チェックするしかけや、同じような効果を持つ 環境変数 と組み合わせるなど工夫が必要です。

4、「$1 $2 $3 …」

「$番号」には、 パターンマッチ でグループ化した値が左から順に代入されます。

#!/usr/bin/perl

$_ = 'Perl/CGI';
/\A(....)(.)(...)\Z/;

print "Content-type: text/html\n\n";
print $1, "<br>\n";
print $2, "<br>\n";
print $3, "<br>\n";
exit;

上記のような場合…。

「$1」には「Perl」。
「$2」には「/」。
「$3」には「CGI」。

以上のように代入されます。

ちなみに…。

「\A」は、文字列の最初にマッチするメタ文字「^」と同じです。
「\Z」は、文字列の最後にマッチするメタ文字「$」と同じです。

5、「$` $& $'」

これも パターンマッチ で使われる特殊変数です。

「$`」は、マッチした文字列の前にあるすべての文字列が代入されます。
「$&」は、マッチした文字列全体が代入されます。
「$'」は、マッチした文字列の後にあるすべての文字列が代入されます。

#!/usr/bin/perl

use strict;

$_ = 'Perl/CGI';
/\W/;

print "Content-type: text/html\n\n";
print $`, "<br>\n";
print $&, "<br>\n";
print $', "<br>\n";
exit;

このPerl/CGIプログラムを実行すると…

「$`」には「Perl」。
「$&」には「/」。
「$'」には「CGI」。

このように代入されます。

ちなみに「\W」とは、英字、数字、アンダーライン以外の文字を表しています。

つまり、「[^a-zA-Z0-9_]」だということです。

6、「$!」

「$!」は、プログラムのエラー番号や、エラーメッセージを格納してくれます。

数値として扱えばエラー番号を、文字として扱えばエラーメッセージを返してくれます。

しかし、動作環境によってはエラーメッセージしか出ない場合もあります。

#!/usr/bin/perl

use strict;

open (IN, './test.txt') or &view("$!");
my $string = <IN>;
close (IN);

&view($string);

sub view {
my ($view) = @_;

print "Content-type: text/html\n\n";
print $view;
exit;
}

例えば上記のような場合だと…。

「No such file or directory」

などという文字が表示されます。

もちろん、「test.txt」がなかった場合ですが…。

open関数 以外にも、「$!」はいろいろな関数で使えますので、わざわざエラーメッセージを用意するのが面倒だという場合には、ぜひ、意図的にエラーを起こすなどして試してみてください。

7、「$/」

「$/」は、入力レコードのセパレータを設定しています。

例えば、行入力演算子の「<」と「>」がありますよね。

これはファイル処理の際、ファイルハンドルをはさむことにより、通常であれば開いたファイルを1行ずつ読み込みますよね。

これは実は、「$/」にあらかじめ改行コードが指定されているからなのです。

つまり、1回ずつ読み込む単位が改行までなので、1行ずつ読み込むと言われているわけなのです。

なので、逆に考えれば「$/」の値を変更してやれば、1回に読み込む単位を変更できるということですよね。

例えば、外部ファイル「test.txt」の中身が以下のようなものだったとします。

1,2,3,
4,5,6,
7,8,9,

そして、Perl/CGIプログラムは以下のような感じだったとします。

#!/usr/bin/perl

use strict;

my $string = "";

{
local $/ = ",";
open (IN, './test.txt') or &view($!);
$string = <IN>;
close (IN);
}

&view($string);

sub view {
my ($view) = @_;

print "Content-type: text/html\n\n";
print $view;
exit;
}

このようにすると「,」単位で読み込むようになります。

上記例では、 スカラ変数 で受けてますから「1,」のみで終了しています。

しかし、 配列 で受けた場合には、「,」単位で最後まで読み込むようになります。

注意点としては、1箇所でも「$/」を書き換えてしまうと、プログラム全体に影響を及ぼしてしまうというところです。

それでもよいという場合は別ですが…。

そうではなく、その部分だけ設定を変えたいという場合には、上記例のように、そのエリアを「{」と「}」ではさみつつ、さらにlocalを使って、「$/」の代入値の有効範囲を制限するようにしま す。

ちなみに「$/」の値を未定義にすると、ファイルの最後まで一気に読み込むようになります。

8、「$.」

「$.」は、読み込み回数をカウントする変数です。

例えば、ファイル読み込みで考えてみましょう。

まずは、同じように外部ファイル「test.txt」を作成します。

中身はこんな感じ…。

1,2,3,
4,5,6,
7,8,9,

次に、Perl/CGIプログラムを作成します。

#!/usr/bin/perl

print "Content-type: text/html\n\n";

open (IN, './test.txt');
while (<IN>) {
print "$. : $_<br>\n";
}
close (IN);
exit;

そして、実行結果はこんな感じ…。

1 : 1,2,3,
2 : 4,5,6,
3 : 7,8,9,

3回読み込み処理をしていることがわかりますね。

さらに、前述しましたように、1回の読み込む区切りを指定しているのは「$/」ですから、この値を変えて遊ぶこともできます。

#!/usr/bin/perl

print "Content-type: text/html\n\n";

{
local $/ = "5,";
open (IN, './test.txt');
while (<IN>) {
print "$. : $_<br>\n";
}
close (IN);
}
exit;

実行結果はこんな感じ…。

1 : 1,2,3, 4,5,
2 : 6, 7,8,9,

お決まりのファイル処理でも、初期設定値を変えてやるとその動きが変化しますからおもしろいですよね。

9、「$"」

「$"」は、配列要素間にあるセパレーター値を格納しています。

どういうことかというと…。

#!/usr/bin/perl

use strict;

my @number = (1..5);

print "Content-type: text/html\n\n";
print "@number";
exit;

実行結果は「1 2 3 4 5」このように、半角スペース区切りで表示されます。

これは、「$"」の初期値が半角スペースだからです。

なので、「$"」の値を以下のように変更してやると…。

#!/usr/bin/perl

use strict;

my @number = (1..5);

{
local $" = ",";
print "Content-type: text/html\n\n";
print "@number";
}
exit;

実行結果は「1,2,3,4,5」となります。

「$"」内の値を変えると、区切り文字も変化することがわかりますよね。

ちなみに、いくら「$"」の値を変更しても、出力時に「"」で配列をはさまなければ、その効果は得られないので注意しましょう。

どういうことかというと例えば…。

#!/usr/bin/perl

use strict;

my @number = (1..5);

{
local $" = ",";
print "Content-type: text/html\n\n";
print @number;
}
exit;

実行結果は「12345」となります。

このように、配列「@number」を「"」ではさまなくなっただけで、「$"」の効力はなくなるということです。

特殊変数の紹介&簡単なまとめについては以上です。

結構大切な部分ですから、しっかり覚えておいてくださいね。

それでは、今回のPerl/CGI学習をはじめましょう!

リファレンスとは?

今回は、リファレンスについて学習していきます。

C言語の世界では、ポインタと呼ばれているやつです。

このポインタが理解できず、C言語を挫折した人間は数知れず?

とかなんとか言われているとかいないとか(笑)。

まぁそのぐらい大切で、難しいところではあるのですが…。

でも、今回学習するPerl/CGIのリファレンスに関しては、C言語のポインタほどのむつかしさはないので、安心してください。

それでは、リファレンスについて学習していきましょう。

そもそもリファレンスとは、いったいなんでしょうか?

リファレンスとは、 変数 に格納されている値そのものをやりとりするのではなく、変数が記録されているメモリ番号のみをやりとりする手法のことです。

いろいろ細かいことを犠牲にしつつ、一言で説明するならこんな感じです。

ものすごく身近な例で説明しますと例えば…。

あなたが、コンビニでお金を支払う様子を思い浮かべてください。

べつにコンビニでなくてもスーパーとか、デパートでもいいですよ。

ようするに、あなたがレジでお金を支払う様子がイメージできれば問題ないです。

それではいきますよ…。

あなたが現金で支払ったときというのが、今まで扱ってきた変数の値のやりとりの手法だとします。

ちょうど、財布の中の現金つまり、変数内の値そのものを受け渡すイメージですね。

これに対して、リファレンスというのは、クレジットカードで買い物してるようなイメージです。

お金のやりとりには違いないのですが、どこにも現金は出てきません。

同じ財布の中でもクレジットカードをやりとりしているだけです。

でも、そのクレジットカードには、あなたの銀行口座つまり、現金のある場所とつながっていますよね。

現金までの道筋情報をやりとりしたわけです。

これがリファレンスのイメージです。

現金つまり値そのものではなく、値までの道筋であるメモリ番号を扱っていくのがリファレンスなのです。

多少強引なたとえですが、なんとなくでも理解していただければうれしいです。

で、ここからがポイントなのですが…。

そのリファレンスを使うことにより、どんなメリットがあるのかを学習していきましょう。

リファレンスを使うメリットは、Perl/CGIプログラム内で、変数をやりとりしなくちゃいけないときに大きな効果を発揮します。

具体的に変数をやりとりするときというのは、一番わかりやすいところではサブルーチンとの間で引数と戻り値を扱うときですね。

引数や戻り値をリファレンス形式で扱うと、2つほどメリットがあります。

1つ目は、変数内の値がどれだけ大きくても、少ないデータ量でやりとりできるという点。

変数内の値ではなくメモリ番号をやりとりするわけですから、データ量は当然少なくなりますよね。

これは、リファレンスの基本的性質でもあるので覚えておきましょう。

2つ目は、スカラ変数、配列変数や連想配列などの変数タイプに関係なく、いくつでもやりとりできるという点。

どんな変数タイプでも、リファレンス形式で渡されるのはメモリ番号ですから、カンマで区切りいくつでもやりとりできるというわけです。

以上が、リファレンスの概要とそのメリットです。

<戻る>

リファレンスの基礎

では、具体的にPerl/CGIプログラムでリファレンスを使ってみましょう。

スカラ変数のリファレンス

まずは、 スカラ変数 のリファレンスからみていきましょう。

#!/usr/bin/perl

use strict;

my $string = 'Perl/CGI';
my $ref_string = \$string;

print "Content-type: text/html\n\n";
print ${$ref_string};
exit;

このPerl/CGIプログラムを実行すると「Perl/CGI」と表示されます。

まずは通常の変数をリファレンス形式にするには、変数の頭に「\」をつけます。

「\$string」という部分がそうですね。

これは変数タイプに関係なく、配列変数でも連想配列でも、リファレンス形式でやりとりするときには、頭に「\」をつけます。

そして「my $ref_string = \$string;」となっていますから、変数「$ref_string」には、「$string」のメモリ番号が代入されたことになります。

次に、リファレンス形式となったスカラ変数の扱い方について見ていきましょう。

「$ref_string」の中には、メモリ番号が入っているだけですから、通常のスカラ変数のようには扱えません。

なので、指定されているメモリ番号の先にある値を参照しにいくように指示を出します。

「${$ref_string}」という部分がそうです。

このように「${リファレンス形式のスカラ変数名}」とすれば、メモリ番号の先にある値にアクセスできるようになっています。

もっと言えば、「{」と「}」はなくてもかまいません。

つまり、「$$ref_string」でもかまわないということです。

スカラ変数のリファレンス形式プログラミングは以上です。

配列変数のリファレンス

次は、 配列変数 でリファレンスを扱ってみましょう。

#!/usr/bin/perl

use strict;

my @alphabet = ('A'..'C');
my $ref_alphabet = \@alphabet;

print "Content-type: text/html\n\n";
foreach (@{$ref_alphabet}) {
print;
}
print $ref_alphabet->[0];
print $ref_alphabet->[1];
print $ref_alphabet->[2];
exit;

このPerl/CGIプログラムを実行すると「ABCABC」と表示されます。

まず、配列変数であっても、リファレンス形式にするときには、頭に「\」をつけます。

ここまでは先ほどと同じですからいいですよね。

次は、リファレンス形式の配列変数の値にどうやってアクセスするかですが…。

まず、配列全体を foreachループ で要素順にアクセスする場合は…。

foreach (@{$ref_alphabet}) {
print;
}

前述のリファレンス形式のスカラ変数の扱いと同じく「{」と「}」でリファレンス形式の値をはさみ、配列変数であることを表すために、「@」を頭につけます。

もちろん、「{」と「}」なしで、「@$ref_alphabet」でもかまいません。

どちらにしてもこれで、配列として扱ってくれるようになります。

配列として扱ってくれるので、特殊変数「$_」に対して、要素が順番に代入され表示されていきます。

このforeachループが前半の「ABC」を表示させている部分です。

これに対して、後半の「ABC」部分は、個別の要素に対してピンポイントでアクセスしています。

print $ref_alphabet->[0];
print $ref_alphabet->[1];
print $ref_alphabet->[2];

リファレンス形式の配列変数の各要素に直接アクセスする場合には、「リファレンスの変数名->[要素番号]」とします。

要素番号もそのまま使えます。

リファレンスの変数名に「->」をつけるだけですから簡単ですね。

以上が、配列変数のリファレンスプログラミングです。

連想配列のリファレンス

次は、リファレンス形式の 連想配列 の扱い方についてみていきましょう。

#!/usr/bin/perl

use strict;

my %alphabet = (
A => 'ONE',
B => 'TWO',
C => 'THREE'
);
my $ref_alphabet = \%alphabet;

print "Content-type: text/html\n\n";
foreach (sort keys %{$ref_alphabet}) {
print $ref_alphabet->{$_}, "<br>\n";
}
exit;

このPerl/CGIプログラムを実行すると…

ONE
TWO
THREE

と表示されます。

頭に「\」をつけて、通常の連想配列をリファレンス形式にするのはいいですよね。

さらに、foreachを使った連想配列リファレンスのループも、前述のリファレンス形式の配列ループと似ているので問題ないでしょう。

特殊変数「$_」には、連想配列のキーがアルファベット順に代入されていきます。

「keys」というのは、連想配列のキーを順に取り出しなさいという意味です。

「sort」というのは、その順番をアルファベット順にしなさいという指定です。

そして、リファレンス形式の連想配列各要素にアクセスするには、「リファレンスの変数名->{キー}」とすればよいわけです。

例えば、「TWO」という値にアクセスするためには「$ref_alphabet->{B}」とすればよいわけです。

これで、リファレンス形式の連想配列プログラミングは以上です。

リファレンスの応用

ここでは、リファレンスを応用して少し遊んでみましょう。

どれも基本的なことですから、応用とまではいえないかもしれませんが…。

まぁでも、リファレンスを使うとこんなこともできるという一例として覚えておいてください。

二次元配列

リファレンスを使うと、C言語でいうところの二次元配列を作ることができます。

二次元配列と聞いただけで、やる気をなくす人が何人かでてきそうですが…。

ここでは少し触れるだけですから、まぁおつきあいください。

そもそも二次元配列というのは、いったい何かといいますと…。

今までこのPerl/CGIプログラム学習で使用してきました配列というのが、一次元配列です。

イメージ的な話をすると、単純に値が横に伸びて並んでいるだけですよね。

これに対して二次元配列というのは、高さが加わった正方形や長方形といったイメージです。

もっと言うなら一次元配列をいくつか積み重ねたような感じといった方がわかりやすいでしょうか?

二次元配列とは、そんなイメージを持った変数の集合体です。

それをリファレンスを使って表現してみようというのがここでのテーマなわけです。

#!/usr/bin/perl

use strict;

my @one = (1, 2, 3);
my @two = (4, 5, 6);
my @three = (7, 8, 9);
my @number = (\@one, \@two, \@three);

print "Content-type: text/html\n\n";
for (my $i=0;$i<=$#number;$i++) {
for (my $j=0;$j<=$#{$number[$i]};$j++) {
print $number[$i][$j];
}
print "<br>\n";
}
exit;

このPerl/CGIプログラムを実行すると…。

123
456
789

と表示されます。

それではひとつひとつ説明していきましょう。

まずは、配列「@one」「@two」「@three」への値の代入はわかりますよね。

この3つは普通の配列変数です。

問題は、配列「@number」ですよね。

要素を3つ作成し、それぞれ値を代入させていますが、どれも、前述の配列をリファレンス形式にしたものを指定していますよね。

「my @number = (\@one, \@two, \@three);」という部分ですね。

この部分で二次元配列を作成しています。

1つ目の要素には、1と2と3。
2つ目の要素には、4と5と6。
3つ目の要素には、7と8と9。

それぞれの要素はリファレンス形式なので、実際の値ではなくそれぞれの配列変数の値につながるアドレスが格納されます。

これで、二次元配列完成です。

しかし、やはり値そのものをやりとりした方がわかりやすいという場合もありますよね。

その場合は…

「my @number = ([@one], [@two], [@three]);」

と書けば値そのものをやりとりできます。

ようするに…

「my @number = ([1, 2, 3], [4, 5, 6], [7, 8, 9]);」

ということです。

次は、forループについて説明していきましょう。

相手は二次元配列ですから、forループも二段構えです。

for (my $i=0;$i<=$#number;$i++) {
for (my $j=0;$j<=$#{$number[$i]};$j++) {
print $number[$i][$j];
}
print "<br>\n";
}

最初のforループはわかりますよね。

通常配列「@number」の要素の数だけくりかえし処理させるように指定しています。

問題は次のforループですね。

配列「@number」の各要素はすべて、リファレンス形式の配列になっています。

なので最初のforループで指定された要素番号「$i」のリファレンスを分析し、その先にある配列内の要素数を参照しにいくように書かなきゃいけないわけです。

ようするに、配列「@number」の要素に対してピンポイントでアクセスしつつ、それがリファレンスであることを示せばよいわけです。

なので、2つ目のforループの最大値は「$#{$number[$i]}」となるというわけです。

もちろん「$#$number[$i]」でもかまいません。

これで、二次元配列に対応したループが完成しました。

次に、二次元配列の値に対してピンポイントでアクセスする方法を説明します。

前述のPerl/CGIプログラム例では、「$number[$i][$j]」となっていますよね。

「$i」と「$j」、それぞれ何を表しているかわかりますか?

「$i」は、配列「@number」の要素番号です。
「$j」は、配列「@number」の各要素に格納されている、リファレンス形式の配列が持つ要素番号です。

なので「$number[0][0]」から、「$number[2][2]」まで変化する間に、9つの値を表現するわけです。

例えば、「8」という値にアクセスするには「$number[2][1]」とすればよいわけです。

では、「5」にアクセスするには?

簡単ですよね。

こんな感じで二次元配列をプログラミングしていくわけです。

最後に、上記のPerl/CGIプログラムの二次元配列をわかりやすく書き直してみました。

#!/usr/bin/perl

use strict;

my @number = ([1, 2, 3], [4, 5, 6], [7, 8, 9]);

print "Content-type: text/html\n\n";
foreach (@number) {
foreach (@{$_}) {
print;
}
print "<br>\n";
}
exit;

実行結果は同じです。

配列変数の数を減らし、forループからforeachループに変えてみました。

配列への代入に値を直接指定していることと、foreachループには特殊変数「$_」が2種類でてきていることがわかれば問題ありません。

編集後記

今回の編集後記は、もう少しリファレンスの話をしたいと思います。

どんな話をするのかというと、先ほどは、配列変数の各要素にリファレンス形式の配列を入れ、なんちゃって二次元配列を作成しましたよね。

これはわかりやすさを重視するため配列変数と配列変数にしましたが、連想配列と連想配列でも同じようなことができます。

ただ、配列変数の場合は値を代入するとき「[1,2,3]」のように「[」と「]」ではさみましたが、ハッシュ配列の場合は「{」と「}」ではさみます。

あとはネスト構造を間違わなければ問題ありません。

ということは、配列変数とハッシュ配列の組み合わせも可能だということですよね。

なので今度は、配列変数の各要素に、リファレンス形式の連想配列を入れてみることにします。

#!/usr/bin/perl

use strict;

my @month = (
{name => 'January', days => 31},
{name => 'February', days => '28 or 29'},
{name => 'March', days => 31}
);

print "Content-type: text/html\n\n";
foreach my $month (@month) {
print "$month->{name}\($month->{days}\)<br>\n";
}
exit;

実行結果はこんな感じ…。

January(31)
February(28 or 29)
March(31)

リファレンスを使うと、このように配列変数と連想配列を組み合わせるといったこともできるわけです。

では、最後に問題!

例えでは、foreachループを使って各要素にアクセスしてますが、そうではなく、ピンポイントでアクセスするにはどうしたらよいかわかりますか?

ここまで学習していれば簡単ですね。

それではこの辺で、今回の学習は以上です。

ありがとうございました。

<戻る>