Prolog 写経記 その 65 read_line/1
(ほぼ) 毎日淡々と Prolog を写経します.元ネタはこちら.
- 作者: ボグダンフィリピッチ,中島誠,伊藤哲郎
- 出版社/メーカー: 海文堂出版
- 発売日: 1990/08
- メディア: 単行本
- 購入: 4人 クリック: 33回
- この商品を含むブログ (68件) を見る
まずは
read_line/1
を写経します.解説
read_line(X)
は現在の入力ストリームにおいて,行の最後までに読み込まれた文字のすべてからなるアトムX
を返す.
ふむ.Java でいうと BufferedReader#readLine()
みたいな.
モード
read_line(-).
ふむ.
定義
では,こいつの定義を写経しませう.
read_line(X) :- get0(Char), get0_chars(Char, ASCII), name(X, ASCII), !. get0_chars(26, []) :- !, seeing(File), assert(eof(File)). get0_chars(10, []) :- !. get0_chars(Char, [Char | ASCII]) :- get0(Char1), !, get0_chars(Char1, ASCII).
うーみゅ...
まずは本体,read_line/1
ですが,ここではまず get0/1
で一文字を読み込みます.
そして get0_chars/2
で行の残りを読み込み,その結果を ASCII
として受け取ります.
それを name/2
でアトムにして返します.
下請けの述語 get0_chars/2
は 3 つの節を持っています.
最初の節は読み込んだ文字が EOF だった場合にマッチするそうです.26... 16 進だと 1A
.今でも有効なんだろうか? ともあれ (JW),その場合は seeing/1
で現在読み込んでいるファイルを取得し,それが eof/1
にマッチするように Prolog データベースに登録しています.うーみゅ...
2 番目の節は読み込んだ文字が行末だった場合にマッチします.10... 16 進だと 0A
.いわゆる '\n'
.その場合は空リストを返します.
最後の節は通常の文字を読み込んだ場合にマッチします.その場合はさらにもう一文字を読み込んで get0_chars/2
を再帰的に呼び出し,その結果の先頭に第 1 引数で渡された文字を連結したものが結果となります.
なんというか,いろいろな意味で微妙.
一番微妙なのは EOF に達したことを示すのに Prolog データベースに述語 (っていうか節) を登録していることですね.
さらに,ここで使われている get0/1
や seeing
などは ISO 標準には含まれていないようで,代わりに get_char/1
などが用意されているようです.
そんなわけで (どんなわけで?),ざっくりと書き換えちゃいましょう.
え? それじゃ写経じゃない? いいのいいの,ただ単に写すことが目的じゃないんだから.
まずは EOF の判定ですが,BufferedReader#readLine()
なんかだと結果を null
で返すことができるわけですが,Prolog の項っていうか,この場合はアトムですが,null
相当のものってあるのでしょうか?
一つ考えられるのは X
を具体化しないでおくということですが,それはそれで扱いにくいかも.
そこで,引数を一つ増やしちゃいましょう.つまり,
read_line2(X, EOF)
を作ります.
試行錯誤した結果,こうなりました.
read_line2(X, EOF) :- get_char(Char), read_rest(Char, ASCII, EOF), name(X, ASCII), !. read_rest(end_of_file, [], true) :- !. read_rest('\n', [], false) :- !. read_rest(Char, [Char | ASCII], EOF) :- get_char(Char1), !, read_rest(Char1, ASCII, EOF).
下請けの述語,get0_chars
はどうも好きになれないので read_rest
に変更.行の残りを読むという気持ちを表現してみました.
ISO 標準の get_char
は入力ストリームが EOF に達すると end_of_file
というアトムを返すそうな.
その他はあまり変わっていません.
注記
入力行の最後にはピリオドは必要でない.も指呼の述語が正しく動作しない場合は,述語
ge0_chars/2
の 2 番目の節中の 10 を 13 に代えてみよ.
ふむ.
10 を 13 へというのは LF を CR にしてみろということね.らじゃあ.
例
では使用例を写経しませう.
filecopy(InputFile, OutputFile) :- see(InputFile), tell(OutputFile), repeat, read_line(L), write(L), nl, eof(InputFile), told, seen.
またまたテスト用の述語が登場.
なのですが,ここでも ISO 標準には含まれていない述語がふんだんに使われています.ちなみに see/1
は入力ファイルのオープン,tell/1
は出力ファイルのオープン,told/1
は出力ファイルのクローズ,seen/1
は入力ファイルのクローズ.わかんないよ.
さらに,EOF の判定に Prolog データベースに eof/1
が登録されることに依存しているので,これも自前の read_line2/2
を使うように変更したバージョンを用意しませう.
filecopy2(InputFile, OutputFile) :- current_input(Stdin), open(InputFile, read, In), set_input(In), current_output(Stdout), open(OutputFile, write, Out), set_output(Out), repeat, read_line2(L, EOF), write(L), nl, EOF = true, close(In), set_input(Stdin), close(Out), set_output(Stdout).
ちょっとくどいかなぁ.
ともあれ (JW),ISO 標準の組み込み述語は他言語になれた身にはわかりやすい名前ですね.
さっそく使ってみます.
40 ?- filecopy2('foo.txt', 'bar.txt'). Yes
できあがったファイルをチェックしたところ,見事にコピーされていました.\(^o^)/
細かいことをいうと,最後に改行が一つ余計に付いちゃうんですけどね.まぁいいや.