2015年01月29日の日記です


プログラム言語における「デフォルト動作」  2015-01-29 16:46:22  コンピュータ

さっき、デフォルトの話書いたのだけど、話の腰を折りそうだったので、書いてから削除した一節がある。


そもそも、過去に書いてたよなー、って思ったら、書いてなかった

じゃぁ改めて書くか。


というわけで、自分が書きたいから書くというだけの、どうでもいい話。




perl には、「デフォルトの動作」がある。

プログラム上、特に書かなかった場合に、勝手に言語が「こうしたいのだろう」と察知して、善きに計らってくれるの。


たとえば、一部の命令では、操作の対象を明記しないと変数 $_ を対象とする。


これが、すごく便利。


というのも、perl の先祖の一つが sed だから。


sed は「ストリームエディタ」の名前の通り、入力に対していろんな処理を施して、出力する。

常に「処理中のデータ」が処理対象なので、変数と言う概念は無い。


で、perl はまともな言語なので、いくつものデータを同時に扱える。

正規表現による置換も、どのデータに対して置換を行うのか明記しなくてはならない。


でも、明記しない場合は $_ を対象とする。

これにより、まるで sed のように処理を書くことができる。




プログラムの話なので、概念だけ書くより実例を挙げよう。


perl の代表的なプログラムに、次のようなものがある。


while(<>){print;}


これは、引数として渡されたファイル名のファイルを開き、内容を行ごとに分割しながら読み込み、その読み込んだ行を標準出力に書きだすプログラムだ。


ちなみに、引数としてファイル名がわたされなかった場合には、標準入力から読み込みを行う。


というわけで、上のプログラムは Unix コマンドである cat と同じ動きをする。



何が省略されているか、もう少し省略しないで書きこんでみよう。


while($_=<>){print $_;}


なんだか <> って命令から $_ に受け取っている。

そして、print では、$_ を書きだしている。


さっき書いたけど、いくつかの命令は、省略されたときに $_ を対象とする。

<> も print も、省略に対して $_ を使っていた、というのがここまででわかってもらえると思う。


問題は <> だ。

ファイルから行単位で受け取る、と最初に説明したのだから、<> がファイルから行単位で読み込み、受け取っているのだ、ということはわかってもらえるだろうか。


foreach $N (@ARGV) {
  open(FILE,$N);
  while($_=<FILE>){
    print $_;
  }
  close(FILE);
}

実はこんな感じになっている。いきなりプログラムらしくなった。


@ARGV というのは、引数として渡されたファイル名のリスト。複数渡すこともできるので、foreach で1つづつ受け取る。

open はファイルアクセスのための準備。FILE にファイルハンドルが入る。


で、<FILE> とすると、ファイルから1行を読み込むわけだ。

うん、やっと全貌が見えてきた。


…でも、じつはこれでは「ファイルが無いときに標準入力から」にはならない。


<> と書いたときは、@ARGV の内容を1つづつオープンし、読み込んでくれる。

@ARGV が無いときは標準入力を使ってくれる。


さっきは、プログラムで同等機能を実現してみたけど、そういうプログラムが省略されていたわけではない。

<> が「善きに計らって」くれていただけだ。




さらに、こんなこともできる。


@_ = <>;


さっきは、while で1行づつ読み込んでいた。

でも、 @_ = <> とすると、while なしで一気にバッファに読み込んでしまう。


@_ がバッファだ。というか、perl では @ で始まるのは配列だ。

1行づつ分解して、配列の中に順次収められている。


代入相手がスカラー変数だと、1行しか読み込めないから、1行づつ処理する。

代入相手が配列変数だと、いくつも入れることができるから、まとめて処理する。


動作が変わるのだから、本来的には命令に対して「スイッチ」みたいな引数があって、そのスイッチで動作が変わる…というのが正しいだろう。

でも、perl は文脈からそれを判断する。人間が細かな設定をしないでも、言語自体が人間のやりたいことを察知してくれるのだ。


こういうのは、「文脈依存文法」といって、人工言語ではあまり美しくないことになっている。

なぜ美しくないかと言うと、文脈に依存するということは、解釈が曖昧になりやすいからだ。


でも、perl はあえて文脈に依存する。

その依存の仕方が、プログラマーが良くやる処理と見事に一致しているため、やりたいことが簡単に出来て気持ち良い、ということになる。



ちなみに、


print @_;


とすると、もちろん配列内容を一気に書きだしてくれる。

もっと言えば、print <>; でも cat になる。




while(<>){
  s/banana/apple/g;
  s/(subuta.*)pineapple(.*)/$1$2/g;
  print if(!/^$/);
}


構造がシンプルとはいえ、プログラムだから改造も出来る。

上のプログラムは、最初に書いた cat のプログラムに、いろいろと処理を追加したものだ。


s/●/▲/g;


というのは、●を▲に置き変える、という意味の sed の命令だ。

perl の命令じゃなくて sed の命令。でも、先に書いたように perl は sed のプログラムをそのまま埋め込めるように設計してある。


ちなみに、本当は


$_ =~ s/●/▲/g;


と書かなくてはならない、 =~ という演算子で、左辺にある変数内容を正規表現によって書き変えるの。


さて、先ほどのプログラムの場合、banana は apple に変わる。

そして、subuta の後ろにある pineapple は取り除かれる。


#ちなみに、僕は酢豚にパイナップルが入っているのは好きだ。


subuta pinebanana buta という文字列があると、「banana」部分が apple に変わり、pineapple になる。

そして pineapple は取り除かれ subuta buta となる。



最後の print の後ろに if が書いてある。

これ、実は「if の条件が満たされたときだけ print せよ」という書き方だ。


perl では、if の後ろは必ずブロックにしなくてはならない。

たった1命令をブロックに入れるのが馬鹿らしい場合、命令の後ろに if を書けばいいことになっている。



で、条件は、やっぱり sed 風の命令だ。 /~/ で、正規表現を書いてある。

ただ、さっきの命令と違って s/ で始まらないことに注意。


これ、「正規表現と一致する」ことを確認するだけで、変数内容を書き変えない。

もちろん、$_ =~ が隠れている。$_ 以外の変数に適用したいときは、ちゃんと書いてやればよい。


というわけで、プリントの条件は /^$/ でないこと。(! は否定の意味)

これは、正規表現で「行の始まり(^)の直後に行の終了($)が来る」ことを意味する。


つまり、空行だった場合は出力しない。




デフォルト動作の話ではないのだけど、perl のエラー処理の「イディオム」は面白い。


~ or die "error";


って書くのね。


これで ~ の部分で失敗したら、プログラムを強制終了する。(die はメッセージを表示して終了する命令。)

「~するか、さもなくば死ね!」って、わかりやすいと思う。


これは、C言語でも使う人いるね。

~ || error();


とか書くと、~部分が false の時だけ error() が実行される。


先に書いた print if ~ もそうだけど、英語として読み下せるようになっているセンスがなかなかだ。




さて、perl 講座ではないので、これくらいでやめにしておく。


perl のプログラムを書くときに、かなり大胆な省略が出来ることがわかってもらえればそれでいい。

少しの例示しかしなかったけど、perl ってこんなのばかり。



省略した時には、「デフォルトの」動作をやってくれる。

この「デフォルト」の決め方がすごく上手で、ちょっと処理したいな、なんて思ったときに、さくっとプログラムが書ける。


もちろん、省略して書いたプログラムは、やりたいことは十分に満たせるけど、性能が悪かったり、かゆいところに手が届いてなかったりするかもしれない。

そういうのは後で改良することになるけど、その際にはあえて細かく書くことで、デフォルト動作をどんどん減らし、自分で書き下すことになる。


究極的には、すべてを自分で設定できるので、「簡単に使えるけど、簡単な処理しかできない」というような言語でもない。




まぁ、この「省略可能」な点こそが、perl が嫌いな人にとっては許せない部分だったりもする。

省略されると、perl の動作に堪能な人しか意味がわからないのだよね。


なんでそのプログラムが動いているのか理解できない、という状態では、自分の目的に合わせて改造したい場合も改造できない。

スクリプト言語だからプログラムのソースもすぐ見られるのに、全く手を出せない、というのはもどかしい。



perl プログラマであっても、他人のプログラムは読みたくない、という人も多い。

いや、自分のプログラムですら、1か月も経ったら読めなくなるという人もいる。


perl はやりすぎかもしれない。

でも、perl 以前は、プログラムと言うのは「厳密に書くもの」で、省略が許される、なんてなかったように思う。


今の言語では、程度の大小はあれど、省略を許すことは結構ある。

先に書いた「デフォルトの言葉の意味」の記事の方に、Javascript や PHP は引数の省略を許す、と書いた。


ただそれだけでも、昔では考えられなかったような「いい加減な」書き方を許しているんだ。


こういう省略、perl がきっかけだったのではないかな、と思う。




僕は昔 perl で CGI 組んでお金頂いていたので、それなりに perl は出来るし、人が書いたプログラムでも問題なく読む。


ただ、今は perl で仕事になることはあまりないので、使ってない。

でも大好き。センスの良い言語。気持ちの上では今も perl 使いだ。



同じテーマの日記(最近の一覧)

コンピュータ

関連ページ

ラリーウォールの誕生日(1954)【日記 13/09/27】

C言語で会話【日記 18/08/06】

プログラム言語における「デフォルト動作」【日記 15/01/29】

別年同日の日記

05年 外構チラシ

09年 ジョウビタキ

10年 加湿器

16年 【追悼】マービン・ミンスキー

16年 AI囲碁

18年 テクニカルサポート


申し訳ありませんが、現在意見投稿をできない状態にしています


戻る
トップページへ

-- share --

0000

-- follow --




- Reverse Link -