この問題は、いくつかのサブ問題に分割すると、はるかに簡単に解決できます。最初にファイルを解析してファイルの直接的な表現にしてから、必要な形でデータベースにロードすることに取り組みましょう。
この種の問題は、定節文法 (DCG) に最適です。この手法を使用して Prolog で複雑な文法を表現するのはごく自然なことであり、差分リストに基づいて効率的な実装が得られます。注意すれば、それらを使用して出力を生成したり、入力を解析したりすることもできます!
まず、非常に役立つdcg/basicsライブラリを入手しましょう。
:- use_module(library(dcg/basics)).
トップダウン方式で文法を実行する方が簡単だと思うので、入力をパーツに分解してみましょう。まず、要素のリストがあります。次に、いくつかの「関数」行があります。あなたが達成しようとしていることのセマンティクスをよく知らないので、これはおそらく悪い名前になるでしょうが、それを試してみましょう.
document(document(Elements, Functs)) -->
element_list(Elements), blanks, funct_list(Functs).
解析の結果は構造体になりますdocument(E, F)
。ここで、E は要素リスト、F は関数リストです。-->
の代わりに を使用していることに注意してください:-
。これは、DCG ルールを定義する方法です。内部的には、Prolog が述語を書き直して、「前」と「後」の差分リストという 2 つの追加パラメーターを与えることになります。
要素はより単純なので、最初に要素を実行しましょう。
element_list([E|Rest]) --> element(E), ",", element_list(Rest).
element_list([E]) --> element(E).
以前に CFG を見たことがあれば、これは非常に直感的なはずです。要素を取得してからコンマを取得し、さらに要素を取得するか、要素のみを取得します。定義しましょうelement
:
element(E) --> [Code], { atom_codes(E, [Code]) }.
phrase/2
これらを今すぐ実際にテストできます。
?- phrase(element(X), "a").
X = a.
よし、それが我々の望む結果だ。1 文字以上の要素を使用する場合は、この定義を拡張する必要がある場合があります。
?- phrase(element_list(X), "a,b,c,d,e").
X = [a, b, c, d, e] ;
false.
これで、 の最初の部分がdocument/2
パーサーから出る途中でどのようになるかがわかった: document([a,b,c,d,e], Functs)
. ファイルのように見えます。これが私たちが望んでいるものです。最初のタスクは、ファイルとそのすべての構造を Prolog が処理できるようにすることです。
次に関数リストを作成しましょう:
funct_list([F|Rest]) --> functp(F), blanks, funct_list(Rest).
funct_list([F]) --> functp(F).
これは要素リストと同じように見えますが、要素の代わりに関数を作成しています。関数を解析するのがどのようなものか見てみましょう:
functp(funct(E1, List)) -->
"funct(", element(E1), ",", whites, "[", number_list(List), "])".
引用符で囲まれた部分は、基本的にリテラル テキストです。繰り返しますが、ファイルを解析する際の柔軟性に応じて、これを調整する必要がある場合がありますが、これは投稿したサンプル入力でうまく機能します。次に、番号リストが必要です。
number_list([N|Rest]) --> number(N), ",", number_list(Rest).
number_list([N]) --> number(N).
繰り返しますが、要素リストと同じです。これは、実際にテストするために必要なすべてです。サンプル テキストを というファイルに入れfile.txt
(実際に持っているものは何でも使用できます)、それを実行しphrase_from_file/2
て解析します。ファイルの最後に予備の改行がないことを確認してください。私たちはそのケースを扱いませんでした。また、3行目にタイプミスがあります(括弧がありません)。
?- phrase_from_file(document(D), 'file.txt').
D = document([a, b, c, d, e],
[funct(a, [1, 2, 3, 4, 5]),
funct(b, [2, 4, 6, 8, 10]),
funct(c, [1, 3, 5, 7|...]),
funct(d, [1, 1, 2|...]),
funct(e, [3, 7|...])]) ;
false.
ビンゴ、ファイルの解析があります。
ステップ 2 は、これを使用してfunct/3
構造を作成することです。を扱う述語を作ってみましょうfunct/2
。要素リストを処理する必要があり、独自のリストを生成します。
do_normalize([E|Es], funct(F,[N|Ns]), [funct(F,E,N)|F3s]) :-
do_normalize(Es, funct(F,Ns), F3s).
do_normalize([], funct(_, []), []).
試してみましょう:
?- do_normalize([a,b,c,d,e], funct(a,[1,2,3,4,5]), X).
X = [funct(a, a, 1), funct(a, b, 2), funct(a, c, 3), funct(a, d, 4), funct(a, e, 5)].
これまでのところかなり良さそうです!
編集して、戻ってきました。
上記の関数は優れてfunct/2
いますが、すべての を生成するには、ファイルから取得した各 に対して使用する必要がありますfunct/3
。でそれを行うことができますmaplist
が、パーサーの出力からそれにブリッジする必要があります。append/2
また、ネストされたリストとして返されるという事実を処理するためにを使用する必要があります。フラット化されたリストが必要です。
normalize(document(Elements, Funct3s), Funct2s) :-
normalize(Elements, Funct3s, NestedFunct2s),
append(NestedFunct2s, Funct2s).
normalize(Elements, Funct3s, Funct2s) :-
maplist(do_normalize(Elements), Funct3s, Funct2s).
それでは、動作するかどうか見てみましょう:
?- phrase_from_file(document(D), 'file.txt'), normalize(D, Normalized).
Normalized = [funct(a, a, 1),
funct(a, b, 2),
funct(a, c, 3),
funct(a, d, 4),
funct(a, e, 5),
funct(b, a, 2),
funct(b, b, 4),
funct(b, c, 6),
funct(..., ..., ...)|...]
これで 2/3 に到達しました。ファイルを正常に読み取り、その内容をデータベースで必要な構造に変換しました。あとは、それらをデータベースに入れるだけで完了です。
最初に Prologfunct/3
が動的であり、実行時に変更できることを伝える必要があります。
:- dynamic funct/3.
forall/2
ループを使用してリストを実行し、すべてをアサートできます。
?- phrase_from_file(document(D), 'file.txt'),
normalize(D, Normalized),
forall(member(Fact, Normalized), assertz(Fact)).
動作していることの証明:
?- funct(X, Y, Z).
X = Y, Y = a,
Z = 1 ;
X = a,
Y = b,
Z = 2 ;
X = a,
Y = c,
Z = 3 ;
X = a,
Y = d,
Z = 4 ;
X = a,
Y = e,
Z = 5
...
それでは、作業全体を 1 つの優れた述語にパッケージ化してみましょう。
load_funct(Filename) :-
phrase_from_file(document(D), Filename),
normalize(D, Functs),
forall(member(Funct, Functs), assertz(Funct)), !.
それを試してみてください:
?- load_funct('file.txt').
true.
そして、あなたは完了です!約23行になりました。
これがお役に立てば幸いです。Prolog を楽しんで使い続けていただければ幸いです。