to be continued ~とあるプログラマーの実験的開発日誌~ -5ページ目

to be continued ~とあるプログラマーの実験的開発日誌~

WEBデザイナー兼プログラマーである管理人が日々のトラブル解決に向けて奮闘する開発日誌。

以前から自サイトで使用していたウェブ貼り付け型RSSリーダーを、より多くの人に利用してもらおうと新たにサイトを開設した。

このサービスは様々なサイトで配信されているRSS情報をひとつにまとめて、ホームページやブログに表示できるというもの。それぞれ別々のサイトから配信されているRSSでも更新順に並べ替えたりできるので、例えば複数のサイトやブログを持つ人なら姉妹サイトの更新情報を一括で表示させたり、ニュースや天気予報などをページに組み込んだりできる。さらに設定でテキストだけや画像だけの表示を選択すれば、画像を並べてギャラリーのような使い方も工夫次第で可能だ。表示の見本として右サイドカラム内に「オススメ情報」として掲載しているので参考にしてもらいたい。
これらのサービスはメールアドレスなどの登録は一切不要で、すべて無料で利用できる。設置方法もコードを自分のホームページやブログに貼り付けるだけなので、興味がある方は是非とも覗いてみてほしい。

複数RSSをひとつにまとめて表示「複合RSS」

前回はHTMLからPerlを呼び出す方法として、インラインフレームを使のが簡単という話だった。
ただし、この方法には問題点があることも前回お伝えした通り。
他にもHTMLからCGIを呼び出すにはSSI(Server Side Include)を利用するなどの方法があるが、使用できるサーバーが限られてくる。
そこで今回はHTMLに記述したJavaScriptからCGIを読み込ませる方法について考えてようというわけだが、その前にアクセスカウンターなど画像だけなら、「<img src="CGIファイルまでのパス">」で簡単に表示させられることを覚えておくと良い。
ただし、CGI側は「Content-Type: text/html」としたハイパーテキストではなく、画像をバイナリモードで出力しなければならないので、以下のように記述する。

GIF画像を出力するサンプル
print "Content-type: image/gif\n\n";
open IMG,"GIF画像へのパス";
binmode IMG;
binmode STDOUT;
print <IMG>;
close IMG;
exit;

1行目はGIF画像用の指定。JPEGなどはまた指定が違うので注意。
3行目はファイルハンドルに対してバイナリモードを指定している。

アクセスカウンターなどを画像で表示するには画像を連結する処理が別に必要だが、「gifcat.pl」というPerl用画像連結ライブラリがフリーで配布され検索すればすぐに入手できるので、そちらを利用するの手っ取り早い。
当然ながら出力が画像以外の場合は表示されないので注意して欲しい。

本題に戻るが、JavaScriptでテキストなどを表示するには次のように記述する。
<script type="text/javascript" src="CGIへのパス"></script>
たった1行でできてしまうのだから、ある意味感動的でさえある。

上記で読み込むCGI側のサンプル
print "Content-type: application/x-javascript\n\n";
print "document.write(\"ここに出力させたい内容\");\n";
exit;

ポイントはContent-typeを「application/x-javascript」とすることで、要するに呼び出したのがJavaScriptなら出力する方もJavaScriptの仕様に合わせろということ。
非常に簡単に思えるかも知れないが、実はここで行き詰る。
テキストだけなら問題ないのだが、画像、つまり「img src=」のようなタグを記述すると、表示されなくなる。
単純にテキストとして出力してくれれば良いのだが、そうはいかないらしい。
アクセスカウンターのようなものならそれでもいいが、少し複雑な表示させようと思うと、これでは困る。
テキストと画像が混在、というかHTML形式として出力するには次のような工夫が必要となる。

JavaScriptのエラーを回避するサンプル
print "Content-Type: text/html;\n\n";
$CONTENTS =<<EOS;
<p>ここに出力ささせたい内容</p>
<img src="画像へのパスf">
EOS
$CONTENTS =~ s/\r//g;
$CONTENTS =~ s/\n//g;
print <<EOS;
document.write('$CONTENTS');
EOS
exit;

2行目から5行目までは、ヒアドキュメントで変数に代入したが、もちろん普通に代入しても構わない(エスケープ処理が必要な点も同じ)。
6行目、7行目は改行コードが含まれているとエラーになるため、削除している。
これで通常通り、HTMLが出力されるはずだ。
JavaScriptでCGIを読み込めると、一気に応用範囲が広がるので、覚えておいて損はない。
今回はHTMLにPerl(CGI)で出力した結果を表示させる方法について考えてみる。
と、その前に

URLで直接CGIを呼び出してブラウザに出力するプログラムのサンプル
#!/usr/bin/perl
print "Content-Type: text/html;\n\n";
print "<html>\n";
print "<head>\n";
print "</head>\n";
print "<body>\n";
print "ここに出力させたい内容";
print "</body>\n";
print "</html>\n";
exit;

1行目の「#!/usr/bin/perl」はPerlへのパスで設置サイト毎に指定されたパスを記述する必要がある。
2行目の「Content-Type: text/html」でハイパーテキスト形式、つまりブラウザで読み込める形式であることを指定している。
以下はHTMLの標準構造に沿って記述したが、表示するだけなら7行目だけでも可能。
これを「test.cgi」などのファイル名でアップロードして、ブラウザで直接アドレスを指定して呼び出してやれば表示されるはずだ。

非常に簡単な方法だが、呼び出し先がHTMLファイルではないため、場合によっては不都合なこともある。
また、この方法ではページ全体を出力するため、例えばアクセスカウンターのようにページの一部分だけを更新させたい場合には効率が悪い。
そこでHTML内にCGIの出力部分を組み込むことで、ページの一部分だけを動的に変化させることを考えてみよう。

ページの一部分だけを変えるのに最も手軽な方法は、インラインフレームを使用することだ。
つまり、先ほどの「test.cgi」をインラインフレーム内で表示させてしまえば良い。

HTMLの記述としてはこんな感じ
<iframe src="test.cgiまでのパス"></iframe>

まあ、大抵はこの方法で何とかなってしまうし、HTML表示部とCGI表示部は完全に独立しているので管理や変更もしやすい。
ただし、幾つか問題点がないわけでもない。
まず、iframeタグというのは非常に使いにくく、表示領域を自動的に調整してくれたりはしないので、表示できる大きさはあくまで固定。HTML側で指定してやらなければならない。
カウンター程度なら大きさを指定してもほとんど問題はないだろうが、内容によってはスクロールバーが表示されたりするので、完全にページと一体化させるのは難しいだろう。
また、iframe内はあくまで別ページであることにも注意が必要だ。例えばiframe内のリンクから移動した場合、リファラーはHTMLのURLではなく、CGIのURLが返ることになる。リファラーを利用したアフィリエイトリンクなどでは整合性が保てなくなる恐れがある。
こうしたことからブログパーツ配布サイトなどではJavaScriptを利用しているのをよく見かける。
確かにJavaScriptを利用してCGIを呼び出せば、表示領域を自動的に調整したり、HTMLの一部として扱われたりと便利なのだが、サーバー上で動くCGIと、ブラウザ上で動くJavaScriptでは、本来まったく性質の違うものなので、JavaScriptからCGIを呼び出すにはちょっとしたコツが必要だ。
それは次回に。

プログラムを書いていると、様々な場面でランダムに要素を抽出したいことがある。
例えば画像をランダムに表示させるなど、一定の確率で無作為に選択するような局面だ。
このような場合、乱数を利用するのが一般的な方法だろう。
仮に配列に複数の要素を格納しておけば、乱数で指定した番号を抜き出すことができる。
Perlには乱数を発生させるものとしてrand関数が用意されているが、単純に
$num = rand(引数);
とするだけでは、なかなか思い通りのランダム要素になってくれない。
(引数は作成される乱数の最大値。10とした場合は0以上~10未満までの間で作成される。10は含まれないので注意)。
乱数は小数点を含む数値となるので、配列の番号とするにはint関数を使って
$num = int(rand(引数));
のように整数にしてやる必要がある。


@listに格納されている要素からランダムに1つの要素を抜き出す例
@list = (A,B,C,D,E,F);
$bar = @list; #リストの数=乱数の最大値
$num = int(rand($bar)); #0~$bar未満までの整数をランダムに作成
$value = $list[$num]; #$numの配列番号の要素を$valueに格納


と、まあ、ここまでは普通に思いつく。
もう少し凝ってみてsplice関数を使って1行で記述してみる。


$value = splice @list,int(rand(@list)),1; #rand()内の@listは上記の$bar=@list を省いたもの


splice関数は通常、配列から指定した要素を取り除いたり、置き換えたりするためのものだが、返り値として取り除いた要素が返ることを利用している。
つまり、上の例で言うと、@listからint(rand(@list))で作成した乱数の番号を1つ取り除き、その取り除いた要素が$valueに返っているのである。


ここからが実は今回のポイントで、1つの要素をランダムに取り出すだけならこれで良いのだが、複数の要素をランダムに取り出したい場合、すなわち複数の乱数を取得する方法を考えてみたい。


まず単純にforeachで繰り返す方法。
#0~99の中から10個の値をランダムに取得
foreach (1..10)
{
push(@value, int(rand 100));
}


配列@valueには0~99のうち、10個の値がランダムに格納されている。
一見、問題なさそうだが、実はこの方法だと値が重複する可能性がある。
重複させないためには一度作成した乱数を取り除いた配列から再び乱数を作成してやれば良いことに気づく。
取り除くといえば、先ほどのsplice関数の出番だ。


#0~99の中から重複しない10個の値をランダムに取得
@list = (1..100); #リスト構成演算子で1~100までのリストを作る
@list = map { $_ - 1 } @list; #リスト構成演算子が0から始まらないのでmap関数で全ての要素を-1にして0~99のリストを作る。面倒くさい。もっと簡単な方法がありそうだが。
foreach (1..10)
{
push(@value, splice @list,int(rand(100)),1);
}


というか、直接配列から要素を取り出してやればもっと簡単。


#配列@listの中からランダムに3つの要素を取り出す
@list = (A,B,C,D,E,F);
foreach (1..3)
{
push(@value, splice @list,int(rand(@list)),1);

}


先日、突然MT4でmq4ファイルがex4ファイルにコンパイルできなくなった。
どうやらMT4付属のMataEditorの仕様変更によるものらしい。
いろいろと調べてみると、「Program Files」にインストールしていることが原因のようだ。
原因、というか大半の人は「Program Files」にインストールしていはずだろうから不具合と言って差し支えないだろう。
そもそもコンパイルできなくなっていること自体に気づいていない人が大半かも知れない。
何しろ、新しくmq4ファイルを導入でもしない限り、ファイル上では見分けがつかない。
私が気づいたのもコンパイルしたはずのファイルの更新日時が古いままなのをたまたま目にしたからである。

まあ、普通にMT4を使用する分にはあまり関係がないかも知れないが、私のように定期的にインジケーターをアップしている者にとっては、気づかずにいたらと思うとぞっとする。


とはいえ、文句を言ってばかりでも始まらないので、別の場所に再インストールしてコンパイルできるようになるか確認してみる。
結論から言うと、「Program Files」でなければ問題はないようだ。
ただ、フォルダ構成が変わっていて、GCIの場合、インストールフォルダ直下に「experts」フォルダがなくなっていて、「MQL4」フォルダにそれぞれ「Indicators」や「Libraries」がある。他のブローカーのものは確認していない。
また、個人的な意見だがMataEditorの仕様変更も非常に使いづらくなっているように感じた。
特に新しくカスタムインジケーターを作成した際、「Indicators」ではなく、「MQL4」に保存されてしまうのはかなりわずらわしい。設定で変更できるのかも知れないが、現状ではそこまで使いこなせていない。
全体的にMT5への移行を推し進めるため、強引にMT4を統合しようとして起こったトラブルという印象を抱いた。
そうなると今後もこの手の問題は頻発しそうな予感である。