Prolog 写経記 その 66 read_list/1

(ほぼ) 毎日淡々と Prolog を写経します.元ネタはこちら.

Prologユーティリティライブラリ

Prologユーティリティライブラリ

今日は read_list/1 を写経します.

解説

read_list(List) は現在の入力ストリームから List の要素を読み込む.行の最後にくると入力動作が終了する.

ふむ.Java でいうと... BufferedReader#readLine() して String#split(String) するような感じかなぁ.

モード

read_list(-).

ふむ.

定義

では,こいつの定義を写経しませう.

read_list(List) :-
	read_atoms(0, [], List), !.

read_atoms(26, ASCII, List) :- !,
	form_atom(ASCII, [], List),
	seeing(File),
	assert(eof(File)).
read_atoms(10, ASCII, List) :- !,
	form_atom(ASCII, [], List).
read_atoms(_, ASCII, List) :-
	form_atom(ASCII, List1, List),
	get0(Char),
	get_chars(Char, ASCII1, End), !,
	read_atoms(End, ASCII1, List1).

form_atom([], List, List) :- !.
form_atom(ASCII, List, [Atom | List]) :-
	name(Atom, ASCII).

get_chars(Char, [Char | ASCII], End) :-
	32 < Char,
	Char < 127,
	get0(Char1), !,
	get_chars(Char1, ASCII, End).
get_chars(Char, [], Char).

うひゃあ...
写経記史上最長? 手強そう...
一つずつ見ていきますか.


まず,read_list/1 本体はとっても単純.
下請け述語 read_atoms/3 を呼び出しているだけ.


その read_atoms/3 は 3 つの節を持っています.どうやら,最初の引数は読み込んだ文字,第 2 引数はその時点までに読み込んだトークン,第 3 引数は結果のリストみたいですね.
最初の節は読み込んだ文字が EOF だった場合のもの.下請けの述語 form_atom/3 を使って読み込み済みのトークン (ASCII,文字のリストで表現される) を結果のリストに追加し,例によって Prolog データベースに現在の入力ファイルが EOF になったという事実を登録しています.
2 番目の節は読み込んだ文字が改行コードだった場合のもの.既に入力済みのトークン (ASCII) を結果のリストに追加しています.
3 番目の節はその他の文字を読み込んだ場合のもの.ちょっと分かりにくいけれど,「その他の文字」は空白やタブなどの制御コードらしい.最初に read_list/1 から呼び出された場合は 0.この場合はその文字は捨てて次の文字を読み込み,get_chars/3 で読み込んだトークンを第2引数として read_atoms/3再帰呼び出ししています.


下請けの下請け form_atom/3トークンをアトムにしてリストに追加する述語です.
最初の節は,行末またはファイルの終端に達した場合など,トークンが空リストだった場合のもので,それまでに作成済みのリストが結果のリストになります.
2 番目の節では読み込んだトークンをアトムにして,それを既存のリストの先頭に追加したものが結果のリストになります.


もう一つの下請けの下請け get_chars/3トークンを読み込む述語.
最初の節は読み込んだ文字が 33〜126 の範囲にある場合,つまり制御コードでない場合のもの.その場合はさらにもう一文字読み込んで get_chars/3再帰呼び出ししてトークンの残りを読み込みます.その先頭に第 1 引数を連結したものが結果のトークン.
2 番目の節は読み込んだ文字が制御コードだった場合で,トークンは空リストになります.


全体として,述語の中で次の文字を読み込むのではなく,読み込んだ文字を引数としてもらい,述語の中で読み込んだ文字は次の述語呼び出しに渡すというのが分かりにくい感じですね.さらに,読み込んだ文字が制御コードだとそれを (出力) 引数として持って帰ったり (Ent のこと).
C なんかだと先読みして制御コードだったら ungetc() でストリームに戻したりできるわけですが,そうしていないのでややこしくなってる気がします.

注記

入力行の最後にはピリオドは必要でない.空白がしきりとされる.リストの要素はアトムであると仮定している.もしこの述語が使用している Prolog で動作しない場合は,述語 read_atoms/3 の 2 番目の節中の 10 を 13 に代えてみよ.

ふむ.
10 を 13 へというのは LF を CR にしてみろということね.らじゃあ.

では使用例を写経しませう.

13 ?- read_list(L).
: field1 numeric reverse
L = [field1, numeric, reverse] Yes

ふむ.


ということで「6 章 入力ユーティリティ」終了です.
なんか,イメージが違うなぁ.学習したかったのはこういうことじゃないんだけど.
なんというか,出来の悪い手続き型プログラミングになってしまってるような気のせいがします.
まるっきり論理的でも宣言的でもない感じ.なんだかなぁ.