你好,我试图创建一个代码,从输入文件中找到一个给定的字符并打印出它的位置,这里是我出来的"在类似问题的帮助下我和#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:参数没有充分实例化
答案 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
即可轻松实现。但是,下面的评论指出了两个问题:
lazy_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.
答案 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
通常是不必要的。但是,我仍然强烈建议阅读上面链接的字符串手册部分。