もしも文字列に式を埋め込めたならば

素数をたくさん求めたいなぁと思って、Perlでこう書いてみた。

@p=(2);
my $n=3;
while(@p<10000){
my $l=sqrt($n);
for(@p){
last if($n%$_==0);
if($_>=$l){
push @p,$n; last;
}
}
$n++;
}

エラトステネスのふるいを使ってるんですよね。
$nが素数であるか確かめるには、sqrt($n)未満の素数で割り切れないか確かめればよいと。
そこまで割り切れることなくたどり着けば@pにpushされると。
最近のPCは高性能だからこんなのも一瞬で計算できますよ。
ちなみに10000番目の素数は104729でした。


以前から気になってたのだが、Rubyではこんなことができる。

puts "料金は#{ (p*1.05).round }円です"

普通、#{i}のように変数名を入れて使いますけど、
どうも式を入れてもOKらしいです。結構便利ですね。
ちなみにPerlには四捨五入するいい関数ってないんですよね。
強いて言えばint関数だけど、int関数は実は結構ひどい関数なんですよね。

print int( (0.725/0.05)+0.5 ),"\n";

0.725/0.05は14.5になるはず。四捨五入すれば15ですね。
四捨五入の時0.5加えて、小数点以下を切ればいいと。
だけど実際はそうはならない。有限桁の10進小数も、コンピュータ内部で使う2進数にすれば無限小数になることあるから。
そのときに微妙に誤差が発生して、14.9999999…となってるのね。
これで小数点以下を切り捨てると14ですよね。まぁこういう都合があると。
で、どうも調べると、CPANからMath::Roundモジュールを入れろと。

perl -MCPAN -e "install Math::Round"
ppm install Math::Round

普通は上の方法でOKだと思うんだが、ActivePerlではなぜか知らんが失敗する。
ppmコマンド使えばいいんだが、ppmで何でも入るわけではないし…
まぁこれなら賢くやってくれますねと。
それでやっとさっきのRubyのプログラムと同じ事が出来て、

print "料金は",round ($p*1.05),"円です\n";

こんな風になると。
round関数がMath::Roundの四捨五入。他にもいろいろあるけどね。


さて、ここからが本題で、Rubyでは#{ }内に式を入れることができた。
けどPerlでは”本体価格は$p円です”のように変数を入れることはできても、式はできない。
まぁそんなもんかと思ってたのだが、こんな方法でできるというのを見た。

print "料金は@{[ round ($p*1.05) ]}円です\n";

#{ }同様のことが、Perlでは@{[ ]}でできると。
一体これは何のことかというと、[ round ($p*1.05) ]という配列リファレンスの指す配列を表示していると。
これは知らなかったのだが、実は配列も”配列の中身は@aである”のように書けば展開されると。
$aという配列リファレンスの指す配列も@$aとか書けば展開されると。
そして配列リファレンスは[ ]で直接用意することができる。
@[ 1,2,3 ]などでは何のことかわからないので、@{ [ 1,2,3 ] }とすれば配列に戻せる。
そしてこういう表記で””内に埋め込めばいいと。
えらいもんですねー。まさかこんな仕掛けがあるとは。
そういう仕掛けなので、@aの要素数を埋め込みたいからといって、”@{[ @a ]}個の要素があります”など書くと、
[@a]は@aと同じ要素を持った配列のリファレンスになってしまうから、@aの中身がでてしまう。
なので、@{[ scalar(@a) ]}とか、@{[ @a+0 ]}とかを埋め込むようにしないといけない。


奇妙な仕組みですが、便利かも知れませんねぇ。
例えばヒアドキュメント内部で使うとか。

my @clock=localtime();
print <<"EOF";
本日は@{[ $clock[5]+1900 ]}年@{[ $clock[4]+1 ]}月$clock[3]日(@{[ qw(日 月 火 水 木 金 土)[ $clock[6] ] ]}曜日)です。
ただいまの時刻は$clock[2]時$clock[1]分$clock[0]秒です。
EOF

実はlocaltime関数の結果は結構取り扱いめんどくさいのよね。
年は1900足さないとだめだし、月も1足さないとだめだし、曜日は漢字に当てはめないと。
こういうのを変数あらかじめ用意しておいてやるのってめんどくさいしね。
ちょっとした式を入れるだけなら@{[ ]}は便利ですね。
ちなみにqw(日 月 火 水 木 金 土)[ $clock[6] ]は、曜日の漢字を並べた配列の$clock[6]番目と。
実は月数が0から始まるのは、qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)[ $clock[4] ]とかするためです。
まぁ1から始まるにしても、この場合は1引いておけばいいだけですけどね。
ちなみにPHPにはヒアドキュメント内に式を埋め込める仕組みはないらしい。
だけど、短縮表記を許可してればの短縮形としてというのが使える。
をと書けるおまけですよ。
なので、HTML部分に円などと埋め込めますよ。
ちなみにPHPのroundは不思議な動作をするとか。だから意図した結果と違う結果が出るかも。
このネタを発見した人はPerlでCGI作るときにHTMLをヒアドキュメントで用意してたと。
この中で使いたかったという話ですから、これと同様のことをPHPでやるなら上のことかなと思ってね。