2

您好,我正在尝试创建一个代码,该代码将从输入文件中找到给定字符并打印出它的位置,这就是我“在我发现的类似问题的帮助下”得出的结果

process2(Text,POS):-
    open('houses.txt', read, In),
    get_char(In, Char1),
    find(Char1, In,Text,POS),
    close(In).

find(Text,In, Text, 0).
find(Char,In,Text,POS) :-
    POS is POS1 +1,
    get_char(In, Char2),
    find(Char2,In,Text,POS1).

但是编译器会抛出此错误:错误:is/2:参数未充分实例化

4

4 回答 4

2

处理输入时,始终首先考虑 DCG:

:- 使用模块(库(pio))。

process3(文件,文本,POS):-phrase_from_file(查找(文本,POS),文件)。

查找(文本,[P|Ps])-->
    lazy_list_character_count (P), 文本, !,
    查找(文本,PS)。
查找(文本,Ps)-> [_],查找(文本,Ps)。
查找(_Text,[])-> []。

这会找到输入字符串的所有位置:

?- process3('/home/carlo/.swiplrc', `file`, P).
P = [51, 174, 254, 452, 549, 1977, 2106, 3682, 4033|...] ;
false.

按照鲍里斯的建议进行编辑,削减可能会删除一些合法的解决方案。所以这里有一个免费的版本。

find(_Text, []) --> [].
find(Text, [P|Ps]) -->
    lazy_list_character_count(P), Text,
    find(Text, Ps).
find(Text, Ps) --> \+Text, [_], find(Text, Ps).
于 2016-03-27T18:36:14.093 回答
1

一种非常干净的方法是使用 DCG,例如CapelliC 的答案。像他一样,使用library(pio)Ulrich Neumerkel 的漂亮方法,例如在 SWI-Prolog 中找到的,您可以将 DCG 和phrase_from_file/2以下解决方案结合起来:

:- use_module(library(pio)).

... --> []|[_], ... .

file_pattern_pos(File, Pattern, Pos) :-
    phrase_from_file(( ...,
                       lazy_list_character_count(Pos),
                       Pattern,
                       ...
                     ),
                     File).

这是从文档中phrase_from_file/2的代码示例逐字获取的,刚刚添加lazy_list_character_count//1。与其他 DCG 答案不同,它在回溯时生成所有解决方案。所以有了这个文件:

$ cat banana.txt
banana
Antananarivo

你从顶层得到:

?- file_pattern_pos("banana.txt", "ana", Pos).
Pos = 1 ;
Pos = 3 ;
Pos = 10 ;
Pos = 12 ;
false.

列出单个字符的所有位置:

?- bagof(P, file_pattern_pos("banana.txt", "a", P), Ps).
Ps = [1, 3, 5, 10, 12, 14].

这个解决方案很好,因为只需查看文档中的代码示例即可轻松获得phrase_from_file/2。但是,在下面的评论中指出了两个问题:

  1. 效率问题;
  2. Usinglazy_list_character_count//1意味着您不能将其与phrase/2.

正如评论中指出的那样,可以解决效率问题:

... --> [].
... --> [_], ... .

另一个问题更严重。毕竟,可能有必要计算消耗的字符数。例如:

span(N) --> span_(0, N).

span_(N, N) --> [].
span_(N0, N) --> [_],
    {   N1 is N0 + 1
    },
    span_(N1, N).

现在,我们可以从顶层编写:

?- phrase_from_file(( span(Pos), "ana", ... ), "banana.txt").
Pos = 1 ;
Pos = 3 ;
Pos = 10 ;
Pos = 12 ;
false.

或者,使用phrase/2

?- phrase((span(P), "ana", ...), "banana").
P = 1 ;
P = 3 ;
false.
于 2016-03-27T20:22:00.263 回答
1

如果您只使用 CLP(FD) 约束而不是低级算术,这将完全按预期工作:

:- use_module(library(clpfd)).

find(Text, In, Text, 0).
find(Char, In, Text, POS) :-
        POS #= POS1 + 1,
        get_char(In, Char2),
        find(Char2,In,Text, POS1).

CLP(FD) 版本的一个优点是它是尾递归的,这也是您直观所做的。

在处理文件时,我也推荐setup_call_cleanup/3,甚至更好。library(pio)(注意:在 SICStus Prolog 中,您可以将 Prolog 标志设置double_quoteschars,然后使用 DCG 将文件处理为字符!如果您对此感兴趣,请在 SWI 中寻求支持!)

我留下这个,并在代码中修复剩余的(终止)问题,作为练习。

于 2016-03-27T20:04:46.027 回答
0

如果您致力于使用 SWI-Prolog,您可以在更简单的情况下使用字符串来处理文本。在这种情况下,例如,使用从流中读取文件In(如您在问题中所见)就足够了read_string/3,并使用以下方法找到其中所有出现的子字符串的位置sub_string/5

setup_call_cleanup(open(File, read, In),
                   read_string(In, _, File_contents),
                   close(In)),
sub_string(File_contents, Pos, _Length, _After, Substr)

而已。Pos将是 的基于 0 的位置Substr。要查找字符,只需使用长度为 1 的字符串。最好的事情之一sub_string/5是它可以正确处理部分重叠的子字符串:

?- sub_string("banana", Pos, _, _, "ana").
Pos = 1 ;
Pos = 3 ;
false.

更标准的对应物sub_string/5is sub_atom/5,具有与 相同的语义sub_string/5,但采用原子。它应该在每个 Prolog 实现中都可用。

?- sub_atom(banana, Pos, _, _, ana).
Pos = 1 ;
Pos = 3 ;
false.

将整个文件读取为代码后,只需使用atom_codes/2然后sub_atom/5. 然而,这有点浪费。

一旦你必须对文件内容做更复杂的事情,你就可以转而使用 DCGlibrary(pio)等。回到使用读取原语get_char通常是不必要的。但是,我仍然强烈建议阅读上面链接的字符串手册部分。

于 2016-03-28T20:06:00.747 回答