用于从输入文件中读取行的 SWI-Prolog 谓词
SWI-Prolog predicate for reading in lines from input file
我正在尝试编写一个谓词来接受输入文件中的一行。每次使用它时,它应该给出下一行,直到到达文件末尾,此时它应该 return 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.
还是我这样做完全错了?也许有一个内置规则可以简单地做到这一点?
如果您想阅读代码列表,请参阅 library(readutil),尤其是 read_line_to_codes/2
,它正是您所需要的。
你当然可以使用character I/O primitives,但至少要使用ISO谓词。 "Edinburgh-style" 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
和 library(readutil).
中的其他谓词
自从 strings 被引入 Prolog 以来,有一些新的漂亮的阅读方式。例如,要读取所有输入并将其拆分为行,您可以这样做:
read_string(user_input, _, S),
split_string(S, "\n", "", Lines)
请参阅 read_string/5
中的示例以按行阅读。
PS。删除 see
和 seen
等。而是:
setup_call_cleanup(open(Filename, read, In),
read_string(In, N, S), % or whatever reading you need to do
close(In))
在 SWI-Prolog 中,最优雅的方法是首先使用 DCG 来描述 "line" 的含义,然后使用 library(pio)
将 DCG 应用于文件。
这样做的一个重要优点是,您可以轻松地将 相同的 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.
这种方法也可以很容易地扩展以解析更复杂的文件内容。
我正在尝试编写一个谓词来接受输入文件中的一行。每次使用它时,它应该给出下一行,直到到达文件末尾,此时它应该 return 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.
还是我这样做完全错了?也许有一个内置规则可以简单地做到这一点?
如果您想阅读代码列表,请参阅 library(readutil),尤其是 read_line_to_codes/2
,它正是您所需要的。
你当然可以使用character I/O primitives,但至少要使用ISO谓词。 "Edinburgh-style" 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
和 library(readutil).
自从 strings 被引入 Prolog 以来,有一些新的漂亮的阅读方式。例如,要读取所有输入并将其拆分为行,您可以这样做:
read_string(user_input, _, S),
split_string(S, "\n", "", Lines)
请参阅 read_string/5
中的示例以按行阅读。
PS。删除 see
和 seen
等。而是:
setup_call_cleanup(open(Filename, read, In), read_string(In, N, S), % or whatever reading you need to do close(In))
在 SWI-Prolog 中,最优雅的方法是首先使用 DCG 来描述 "line" 的含义,然后使用 library(pio)
将 DCG 应用于文件。
这样做的一个重要优点是,您可以轻松地将 相同的 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.
这种方法也可以很容易地扩展以解析更复杂的文件内容。