お手軽であるということは大切である

実は、夏休みの課題で、
テキストファイルにデータがたくさんあるので、それの平均値やらを求めろと。
平均値だけならかなり簡単だが、標準偏差も求めんといかんので。
そんなものは表計算の関数を使えばすぐ終わるが、まぁそこはきちんと考えなさいと。
で、Perlでやったと。3分ぐらいでとりあえずは作れたから、結構早いと思う。
ただ、ちょっとこのまま提出するのはあれなので、バグ取りに時間を食ったけど。


そもそも、Perlというのはかなり下品だけど使いやすいものです。
それは配列や文字列の扱いにしてもそうで、やはり扱いやすい。
Cではそんなもの扱おうとすると、安全装置ばっかりになってえらくめんどくさい。
そんなわけで、Perlで書いたわけだ。
さらにperl 5.8以降、マルチバイト文字列への対応がよくなったので、これを使わない理由はないですね。
そのへんのお話は、以前書いた、「マルチバイトを知ったperlは便利」の記事参照。
ところが実はこの記事には少し誤りがあったというわけで書いておきます。

#!/usr/bin/perl
use encoding 'shiftjis';
#binmode(STDOUT, ":encoding(shiftjis)");
#binmode(STDIN, ":encoding(shiftjis)");

UTF-8以外で書いたPerlのスクリプトの場合注意してください。
binmodeは書かない。use encodingで示したのにセットされます。
しかし、それでは都合が悪いこともあるかもしれない。
もちろんUTF-8でスクリプトを書けばいいのだけど、こんな解決法もあると。

use encoding 'utf8',STDOUT=>'shiftjis';

バグ取りと言ってたけど、この事実を知らなかっただけです。
ちょっと注意してください。
その点以外は実にスムーズでして、foreachを活用してうまいこと書けました。
もっとも、今回のはutf8フラグを立てる必要はなかったんだけどね。けど気持ち悪いし。


ところで、Cで書くと、安全装置だらけになると言いましたが、実際の様子をみればわかるでしょう。

fp=fopen("data.txt","r");
if(fp==NULL) abort();
while(!feof(fp)){
str[0]='\0';
fgets(str,32,fp);
l=strlen(str);
if(l==0) continue;
if(str[l-1]!='\n'){
if(!feof(fp)) abort();
}
if(sscanf(str,"%d",&v)==1){
a+=v;
n++;
}
}

まず、この前提として、1行に1つだけ整数が入ってると。
もし1行に2つあっても1つしか読み取らない。そんな前提なのでやたらめんどくさい。
安全のためにはこちらの方がよいでしょう。
まず、ファイルポインタがNULLでないか確認。ここでNULLならabortでとりあえず待避。
fgetsの話は以前もしたように思うけど、getsではメモリ破壊が起きてしまう。
だからfgetsを使って、メモリ破壊を防ぐ。
すると尻が切れる可能性がある。なので、fgetsの性質で、最後に改行も入るというのがあるから確認する。
それで改行があるか確認して、なければabort。
それでsscanfでその文字列を解析する。ここで1つ読み取ることに成功しなければ値を加算しない。
ところがこのままでは最終行付近でおかしなことが起きる。
まず、最終行が何もないとき。だから、最後のデータのあと改行だけで終わってるときです。
このとき、なぜかfgetsで読み取らない。それで文字列の内容が変わらない。なので同じデータを2回読む。
だから、1文字目のところを’\0’にして、強制的に文字列の長さを0にして、0のままならやり直しと。
といっても、これは最終行以外はありえないことなので、実質そのためだけですね。
改行の後に余分な空白などがあったら、そこから整数が読み取られなければ、値を加算しないだけで特に問題なし。
もう1つは最後のデータのあと改行無しのとき。このときは最後に改行が入らない。
しかしそのときはEOFに到達してるはずだから、確認して、到達してなければabortと。


ここまでするかというかも知れないけど、まぁこの程度はしないといかんのよね。
今回はCでやること前提のデータなのでまだましですが、
1行に2つ1組あるときとか考えるとなかなかこうもいかんでしょう。
Perlであればかなりこの辺の融通は効きますね。
文字列処理は非常に強いから、かなりいける。
まぁとんでもない性能を要求するならともかく、Cでやるメリットは少ないでしょう。それにそんなことはない。
ならPerlやらでパッとスクリプトを書いて、うまいこと動いてくれることを期待して待っておけばいい。
本当にCでプログラム書くと、しょっちゅうメモリ破壊起こすからね。
少しならあまり影響ないとはいえ、それは偶然。やはりできるだけの工夫はしないとね。
まぁ、これについてはCの配列の仕組みは極端にプログラマ任せなせいだけどね。
その辺管理してくれるものはいいし、Perlの配列は可変長だからね。これもいい。