在prolog中搜索文件

时间:2016-03-27 17:26:11

标签: io prolog instantiation-error

你好,我试图创建一个代码,从输入文件中找到一个给定的字符并打印出它的位置,这里是我出来的"在类似问题的帮助下我和#39 ;发现"

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).

然而编译器抛出此错误: 错误:是/ 2:参数没有充分实例化

4 个答案:

答案 0 :(得分:2)

处理输入时,请先考虑DCG:

:- use_module(library(pio)).

process3(File, Text, POS) :- phrase_from_file(find(Text, POS), File).

find(Text, [P|Ps]) -->
    lazy_list_character_count(P), Text, !,
    find(Text, Ps).
find(Text, Ps) --> [_], find(Text, Ps).
find(_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).

答案 1 :(得分: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_quotes设置为chars,然后使用DCG将文件作为字符处理!如果您对此感兴趣,请在SWI中寻求支持!)

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

答案 2 :(得分:1)

一种非常干净的方法是使用DCG,如the answer from CapelliC。和他一样,使用Ulrich Neumerkel的漂亮library(pio),例如as found in 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).

这是从documentation to 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. 使用lazy_list_character_count//1表示您无法将其与phrase/2一起使用。
  3. 效率问题可以在评论中指出:

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

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

    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.
    

答案 3 :(得分:0)

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

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

就是这样。 Pos将是Substr的从0开始的位置。要查找字符,只需使用长度为1的字符串。sub_string/5最好的一点是它能正确处理部分重叠的子字符串:

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

sub_string/5的标准对应部分是library(pio),其语义与sub_string/5相同,但是使用原子。它应该在每个Prolog实现中都可用。

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

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

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