SWI-Prolog谓词用于从输入文件

时间:2015-07-16 20:39:28

标签: file-io prolog swi-prolog

我试图写一个谓词来接受输入文件中的一行。每次使用它时,它应该给出下一行,直到它到达文件的末尾,此时它应该返回false。像这样:

database :-
    see('blah.txt'),
    loop,
    seen.

loop :-
    accept_line(Line),
    write('I found a line.\n'),
    loop.

accept_line([Char | Rest]) :-
    get0(Char),
    C =\= "\n", 
    !,
    accept_line(Rest).
accept_line([]).

显然这不起作用。它适用于输入文件的第一行,然后无休止地循环。我可以看到我需要有一些像" C = \ = -1"在那里检查文件的结尾,但我无法看到它的去向。

所以输入和输出的示例可能是......

INPUT
this is
an example

OUTPUT
I found a line.
I found a line.

或者我这样做完全错了?也许有一个内置规则可以做到这一点吗?

2 个答案:

答案 0 :(得分:5)

在SWI-Prolog中,最优雅的方法是首先使用DCG来描述“线”的含义,然后使用library(pio)将DCG应用于文件。

这样做的一个重要优点是,您可以轻松地将{em>相同的 DCG应用于使用phrase/2的顶层查询,而无需创建文件来测试谓词。

DCG tutorial解释了这种方法,您可以轻松地根据您的使用情况进行调整。

例如:

:- use_module(library(pio)).

:- set_prolog_flag(double_quotes, codes).

lines --> call(eos), !.
lines --> line, { writeln('I found a line.') }, lines.

line --> ( "\n" ; call(eos) ), !.
line --> [_], line.

eos([], []).

使用示例:

?- phrase_from_file(lines, 'blah.txt').
I found a line.
I found a line.
true.

使用示例,使用相同的DCG直接从字符代码解析而不使用文件:

?- phrase(lines, "test1\ntest2").
I found a line.
I found a line.
true.

这种方法可以非常容易地扩展到解析更复杂的文件内容。

答案 1 :(得分:3)

如果您想阅读代码列表,请参阅library(readutil),特别是read_line_to_codes/2,它完全符合您的需求。

您当然可以使用character I/O primitives,但至少使用ISO谓词。 "爱丁堡式" I / O已被弃用,至少对于SWI-Prolog而言。然后:

get_line(L) :-
    get_code(C),
    get_line_1(C, L).

get_line_1(-1, []) :- !. % EOF
get_line_1(0'\n, []) :- !. % EOL
get_line_1(C, [C|Cs]) :-
    get_code(C1),
    get_line_1(C1, Cs).

这当然是很多不必要的代码;只需使用read_line_to_codes/2和库中的其他谓词(readutil)。

自从strings被引入Prolog以来,有一些新的漂亮的阅读方式。例如,要读取所有输入并将其拆分为行,您可以执行以下操作:

read_string(user_input, _, S),
split_string(S, "\n", "", Lines)

请参阅read_string/5中的示例,了解行格式。

PS。放弃seeseen等。相反:

setup_call_cleanup(open(Filename, read, In),
        read_string(In, N, S), % or whatever reading you need to do
        close(In))