File Prolog I/O读取文件并提取要使用的信息

File Prolog I/O读取文件并提取要使用的信息,file,prolog,File,Prolog,在我的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旅程中,现在我被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 btw


编辑:对不起,我不同意。我不知道尝试使用“funct”是错误的。我纠正了我的期望。functExp/3是我试图获得的输出,它与funct不同。我认为我应该使用assert/assertz内置函数,但我仍然不确定我应该做什么。再次为我的误解感到抱歉。正如你可能猜到的,我还是个新手,所以如果你能耐心听我说,我将不胜感激。

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

这类问题非常适合于定分句语法(DCG)。使用这种技术在Prolog中表达复杂的语法是很自然的,并且您可以得到基于差异列表的高效实现。如果你小心的话,你甚至可以使用它们来生成输出和解析输入

首先,让我们从中获取非常有用的库:

:- use_module(library(dcg/basics)).
我发现用自上而下的方式编写语法比较容易,所以让我们将输入分解为几个部分。首先,我们有一个元素列表。然后我们有一些“funct”行。我真的不知道你想要完成的语义,所以这很可能是个很糟糕的名字,但是我们来试试吧。
document(document(Elements, Functs)) --> 
  element_list(Elements), blanks, funct_list(Functs).
解析的结果将是一个结构
文档(E,F)
,其中E是元素列表,F是函数列表。请注意,我们使用的是
-->
而不是
:-
。这就是定义DCG规则的方式。在内部,Prolog将重写谓词,为其提供两个额外参数:“before”和“after”差异列表

现在让我们先做元素,因为它们更简单:

element_list([E|Rest]) --> element(E), ",", element_list(Rest).
element_list([E])      --> element(E).
如果你以前看过CFG,这应该是非常直观的。我们得到一个元素,然后是逗号,然后是更多的元素,或者只是一个元素。现在让我们定义
元素

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).
这看起来就像元素列表,但我们制作的是函数而不是元素。让我们看看解析funct的感觉:

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\u from\u file/2
对其进行解析。确保文件末尾没有备用换行符;我们没有处理那个案子。另外,第3行有一个输入错误(缺少括号)

宾果,我们有文件解析

第二步是使用它创建
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的路要走。我们已成功读取该文件并将其内容转换为数据库中所需的结构。现在我们只需要将它们放入数据库,我们就可以完成了

我们必须首先告诉Prolog,
funct/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并坚持下去

编辑

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

  • 获取看起来几乎有效的Prolog的输入
  • 将其转换为看起来几乎有效的序言的输出(这里也缺少点)
  • 在Prolog中执行此操作
你没有告诉我们你要用它做什么有用的事情。毕竟,有人会认为你会被解雇
?- load_funct('file.txt').
true.
1 s/^/atoms(\[/
1 s/$/\])/
s/$/\./
funct(A,B,N) :-
    funct(A,Ns), nth1(BN,Ns,N),
    atoms(Bs), nth1(BN,Bs,B).
?- funct(A,B,N).
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).