如果我们把它分解成几个子问题,这个问题会更容易解决。让我们首先尝试将文件解析为文件的直接表示,然后以您想要的形状将其加载到数据库中。
这种问题非常适合定子句语法(DCGs)。使用这种技术在 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 将重写谓词给它两个额外的参数:“之前”和“之后”差异列表。
现在让我们先做元素,因为它们更简单:
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.
很好,这就是我们想要的结果。如果您要拥有多个单字符元素,则可能必须扩展此定义。
?- 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.
宾果游戏,我们有文件解析。
第二步是用它来创建你的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
我们从文件中传入的每个 s 上使用它来生成所有的funct/3
s。我们可以用 来做到这一点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
...
现在让我们将整个作品打包在一个漂亮的谓词中:
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 并坚持下去!