在 GNU Prolog 中,如何从用户读取一行输入,直到命中 EOF?

问题描述

我一直在阅读 GNU Prolog 文档以弄清楚如何读取一行输入直到到达 end_of_file 原子。这是我编写这样一个目标的伪代码

read_until_end(Chars,Out):
   if peek_char unifies with end_of_file,Out = Chars
   otherwise,get the current character,add it to a buffer,and keep reading

我是这样实现的:

read_until_end(Chars,Out) :-
    peek_char(end_of_file) -> Out = Chars;
    peek_char(C) -> read_until_end([C | Chars],Out).

prompt(Line) :-
    write('> '),read_until_end([],Line).

这是 REPL 中发生的事情:

| ?- prompt(Line).
> test

Fatal error: global stack overflow (size: 32768 Kb,reached: 32765 Kb,environment variable used: GLOBALSZ)

如果我为 C 的第二个分支打印出 read_until_end,我可以看到 peek_char 总是给我相同的字符,'b'。我认为我需要一种方法来处理某种类型的输入字符索引或类似的东西,但我在文档中找不到这样做的方法。如果我知道一种方法,我可能不得不使用递归来处理这样的指针,因为我不能有任何可变状态,但除此之外,我不知道该怎么做。有人有什么建议吗?

解决方法

您正在使用 peek_char/1 获取下一个字符,但该谓词不消耗流中的字符(它只是“窥视”流)。因此,您的代码中会发生无限递归,并以全局堆栈溢出结束。

您应该使用 get_char/1 从流中读取和使用字符,并使用 reverse/2 收集的字符列表:

read_until_end(Chars,Out) :-
    get_char(Char),(
     Char = end_of_file -> reverse(Chars,Out)
    ;
     read_until_end([Char | Chars],Out)
    ).

为了避免需要反转列表,您可以稍微修改您的程序以按顺序构建列表(不使用累加器):

read_until_end(Output) :-
    get_char(Char),(
     Char = end_of_file -> Output=[]
    ;
     (
       Output=[Char|NOutput],read_until_end(NOutput)
     )
    ).

prompt(Line) :-
    write('> '),read_until_end(Line).