Perl/CGI研究室 'PERL-LABO'

Perl/CGI研究室 'PERL-LABO' TOPへ
戻る(History.Back)

画像のランダム表示

研究内容

HTTPヘッダで Location を指定すると任意のページにジャンプすることができることを利用して、 画像をランダムに表示するCGIプログラムを作ってみましょう。

結果

ランダムな数字を作るには

ランダムというのはでたらめっていうことです。 ランダムなことをプログラムでするたった1つの方法は、 ランダムな数字、乱数を使うこと。 乱数は、rand 関数で生成することができます。

乱数を生成する rand 関数

rand 関数は次のように使います。

$detarame = rand(最大値);

これで、$detarame には 0〜最大値 の範囲のでたらめな値が入ります。 この値は呼び出すたびに変わります。 この値は小数点以下を含む実数です。

でたらめな整数が欲しい場合は、int 関数との合わせ技が有効です。 int 関数は小数点以下を切り捨てた値を返します。

$detarame = int(rand(最大値));

こうすると、0〜(最大値-1) の範囲の乱数を得ることができます。 切捨てなので、得られるのは 最大値-1 までになることに注意しましょう。

乱数の初期化ができる srand 関数

rand 関数で乱数を作ることができるのですが、 rand 関数が作る乱数は正確にはでたらめではありません。 連続して呼ぶと、あたかも本当にでたらめかのような値が得られますが、 実際はある方法に基づいて計算したでたらめかのような数値列です。 そのため、初期化という処理が必要です。 初期化を行わないと、 プログラム実行後、必ず同じ順番でそのランダムな数値が得られてしまうのです。

この乱数の初期化という処理、Perlでは最初の rand 関数の呼び出しのときに 自動的に行ってくれるらしいです。 その初期化というのは、次のような処理だそうです。

srand(time());

srand というのが乱数の初期化を行う関数です。 引数はある数値。引数に与えた数値によって、生成される乱数列が決まります。 そして、ここでは time 関数の戻り値を srand に渡しています。 time 関数はある固定の時点からの経過秒数を返すんでしたね。 ですので、time 関数の戻り値はCGIプログラムの実行された時間によって異なります。 つまり、CGIプログラムの実行された時間が一致していないかぎり、 以降に生成される乱数列は別のものになります。

…ということは、逆に、 実行された時間が同じ(秒が同じ)の場合、生成される乱数が同じ値になってしまうんですね。 それじゃあ困る、という場合はもうひとひねりしないといけません。

ひとひねり

今回は、 同じページに複数のランダム画像を置きたいと思います。 しかしそうすると、CGIプログラムを起動した時間がほぼ一致し、 どのCGIプログラムも同じ乱数値になって同じ画像が表示されてしまいます。 そこで、ひとひねりして乱数の初期化処理を自前で行うようにします。

CGIプログラムを <img src=rndimg.cgi?1> というように ?数値 付きのURLで呼び出すことにしましょう。 この数値を使って乱数の初期化を行うのです。 そのままの値を使うと、毎回同じ画像になってしまいますので、 srand(time() + $ENV{"QUERY_STRING"}); というようにして、 time 関数と組み合わせます。

これで、同じページから同時に呼び出しても、異なる乱数が得られることになります。

プログラム作成

まず配列に画像ファイルのファイル名を格納しておきます。 最初から配列に決まったデータを入れておく場合は、配列の初期化っていうことで次のように します。

@array = (データ, データ, データ, ...);

そして乱数を生成します。もちろん乱数の初期化を忘れずに。 生成する乱数は、0 から 配列の要素の数-1 までの範囲の整数です。 そのために配列の要素の数が必要ですが、配列の要素数は @配列 でいいんでしたね。

乱数ができたら、あとは Location でその画像にジャンプ!で終了です。

作成したCGIプログラム

rndimg.cgi
#!/usr/bin/perl

@jpg = (
	"1.JPG",
	"2.JPG",
	"3.JPG",
	"4.JPG",
	"5.JPG",
	"6.JPG",
	"7.JPG",
	"8.JPG",
	"9.JPG"
);

srand(time() + $ENV{"QUERY_STRING"});

$n = @jpg;
$i = int(rand($n));

print "Location: http://www.perl-labo.org/location/rndimg/$jpg[$i]\n";
print "\n";

実行結果

これらは各文字で別々のJPGファイルとなっています。下に、これらの画像をランダムに表示します。

ぴったり PERL-LABO になったら凄いですね!

考察

Locationについて

Location による別ページへのジャンプは、このように画像に対してもできるし、 それ以外にもなんでもOKみたいです。

同じ画像が表示されてしまうもう1つの理由

今回の研究では、 同じ時間に起動したCGIプログラムでも異なる乱数を得るために、 <img src=rndimg.cgi?1> のように数値付きで呼び出すという方法を考えました。 しかしちょっと考えてみると、このように呼び出し側で数値を与えなくても、 CGIプログラム側でプロセスIDを使うなどの方法で 同じ時間に起動しても異なる乱数を生成することはできそうです。 しかし、実際に試してみるとうまくいきませんでした。というのは、 例えば<img src=rndimg.cgi><img src=rndimg.cgi>というようなHTMLソースに なっているとき、ブラウザ側はこの2つを同じ画像だと考えて、1回しかrndimg.cgiにアクセスしないのです。 その結果、全部同じ画像になってしまいます。これを回避するために、画像のURLを異なるものにすることが必須です。 CGIプログラム呼び出しで ?1 ?2 といった数値を付けるのは、いずれにせよ必要なのでした。 ということで、今回の作ったCGIプログラムは、まあまあ合格点かな?

研究メモ

  1. rand 関数で乱数を得られます。引数に発生する値の上限を与えます。
  2. 引数を省略すると 0〜1 の範囲の乱数になるそうです。
  3. 返ってくるのは実数値です。
  4. 整数の乱数が欲しい場合は、int 関数との組み合わせが有効です。
  5. 配列は @名前 = (データ, データ, ...); で初期化できます。
  6. srand 関数で乱数の初期化ができます。
  7. srand 関数を呼ばなくても、最初の rand 関数の呼び出しの時に自動的に初期化されます。
    その初期化は srand(time()) という形で行われているそうです。
  8. 配列のサイズを得るには $n = @array; というようにスカラー変数に代入します。
  9. Location ヘッダは、どんなファイルとか関係なくジャンプします。
Perl/CGI研究室 'PERL-LABO' TOPへ
戻る(History.Back)

Copyright (c) 'PERL-LABO' All Rights Reserved.  リンクフリーです。