2

在我在 Prolog 的旅程中,现在我被 i/o 部分困住了。我试图读取以特定格式编写的文件,并用它们创建我的事实并使用这些事实。但是在阅读了 stackoverflow 中的几个库和示例之后,我无法找到答案。例如,这是我的文件。

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,9])
funct(d,[1,1,2,3,5])
funct(e,[3,7,11,19,23])

funct/2 根据第一行中写入的每个元素列出了列表。我试图在开始时获得第一行然后使用它,但我无法做到。我认为这是正确的方法,尽管在现在尝试 Prolog 之后,我对自己不太确定。我想做的是:

functExp(a,a,1)
functExp(a,b,2)
functExp(a,c,3)
functExp(a,d,4)
functExp(a,e,5)
functExp(a,b,1)
functExp(b,a,2)
functExp(b,b,4)
functExp(b,c,6)...

我正在使用 SWI-Prolog 顺便说一句。

编辑:抱歉给您带来不便。我不知道尝试使用“功能”是错误的。我纠正了我的期望。functExp/3 是我想要得到的输出,它与 funct 不同。我想我应该使用 assert/assertz 内置函数,但我仍然不确定我应该做什么。再次对我的误解深表歉意. 正如您可能猜到的那样,我仍然是新手,所以如果您愿意接受我,我将不胜感激。

4

3 回答 3

5

SWI-Prolog 有大量的 IO 内置,结合它们我们可以很容易地读取你的文件数据:

process_file :-
    open('input.txt', read, S),

    read_line_to_codes(S, Fs),
    atom_codes(Fa, Fs),
    atomic_list_concat(Elems, ',', Fa),

    repeat,
    read_line_to_codes(S, L),
    read_from_codes(L, T),
    ( T == end_of_file -> close(S)
    ; apply_line(T, Elems), fail
    ).

apply_line(T, Es) :-
    T =.. [F,K,As],
    maplist(out_line(F, K), Es, As).

out_line(F, K, E, A) :-
    T =.. [F, K, E, A],
    writeln(T).

input.txt 当然包含您显示的数据

于 2013-04-19T10:24:57.283 回答
1

如果我们把它分解成几个子问题,这个问题会更容易解决。让我们首先尝试将文件解析为文件的直接表示,然后以您想要的形状将其加载到数据库中。

这种问题非常适合定子句语法(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/3s。我们可以用 来做到这一点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 并坚持下去!

于 2013-04-19T03:27:17.083 回答
1

编辑

我知道这并不能回答您的确切问题,而是您想做什么,如果我可以总结一下:

  • 接受看起来几乎是有效的 Prolog 的输入
  • 将其转换为看起来几乎是有效 Prolog 的输出(你也错过了那里的点)
  • 在 Prolog 中执行此操作

你没有告诉我们你要用它做什么有用的事情。毕竟,人们会期望你会做一些与funct你想要作为输出的 s 相关的 Prolog。然后将它们推送到标准输出,每行一个(最后没有点)是没有意义的。

这里有一个解决方案:

您的原始文件几乎是正确的 Prolog,那么为什么不使用文本编辑程序来修复它呢?例如,使用 sed,您可以使用:

1 s/^/atoms(\[/
1 s/$/\])/
s/$/\./

对于更复杂的示例,您可能需要对其进行调整,但如果您已经拥有几乎有效的 Prolog,则仍然相对简单。

(注意:原始输入文件的第 3 行缺少右括号,我想这是疏忽)

现在您可以使用consult从 Prolog 加载文件。do_normalize如果您坚持要在数据库中明确地拥有所有关系,您仍然需要像@DanielLyons 显示的那样。但是看看这个规则:

funct(A,B,N) :-
    funct(A,Ns), nth1(BN,Ns,N),
    atoms(Bs), nth1(BN,Bs,B).

funct/3根据原始(更正)数据和您提供的示例进行定义。您可以按照您显示的顺序枚举您需要的所有“规范化”函子,只需说:

?- funct(A,B,N).

或者使用实例化的任意参数查询它。

您当然可以在数据库中声明它们,或者将它们写入文件,如果这是心脏想要的。

于 2013-04-19T06:42:13.960 回答