提问人:Philkav 提问时间:11/23/2009 最后编辑:BenMorelPhilkav 更新时间:2/15/2014 访问量:2790
SWI Prolog - 有条件的不是?
SWI Prolog - conditional NOT?
问:
我正在尝试制作一个 prolog 函数。该函数读取一个句子,然后尝试提取一个关键字。如果找到关键字,它将打印一条消息。如果找不到关键字,我希望它也能打印一条消息。这是我的例子:
contains([word1|_]) :- write('word1 contained').
contains([Head|Tail]) :- Head \= word1, contains(Tail).
contains([word2|_]) :- write('word2 contained').
contains([Head|Tail]) :- Head \= word2, contains(Tail).
contains([word3|_]) :- write('word3 contained').
contains([Head|Tail]) :- Head \= word3, contains(Tail).
上面的代码将检查并查看提取的单词是否存在。但是,如果不包含单词“word1,word2 或 word3”,则不会给出答案。有谁知道我应该如何实现它?
我尝试添加:
contains([_|_]) :- write('nothing contained'),nl.
contains([Head|Tail]) :- Head \= _, contains(Tail).
但显然这是错误的做法。
答:
2赞
liori
11/23/2009
#1
在祈使式语言中,你会使用某种标志;例如:
found = False
for word in wordlist:
if word in ('car', 'train', 'plane'):
print "Found: " + word
found = True
if not found:
print "Nothing found."
您可以将此标志作为子句的另一个参数实现:
% entry point
contains(X) :- contains(X, false).
% for each word...
contains([Word|Rest], Flag) :-
Word = car -> (write('Car found.'), nl, contains(Rest, true)) ;
Word = train -> (write('Train found.'), nl, contains(Rest, true)) ;
Word = plane -> (write('Plane found.'), nl, contains(Rest, true)) ;
contains(Rest, Flag).
% end of recursion
contains([], true).
contains([], false) :- write('Nothing found.'), nl.
如果要为每个单词创建不同的子句(并抽象循环),请将中间部分更改为:
% for each word...
contains([Word|Rest], Flag) :-
checkword(Word) -> NewFlag=true ; NewFlag=Flag,
contains(Rest, NewFlag).
% and at the end:
checkword(car) :- write('Car found.'), nl.
checkword(plane) :- write('Plane found.'), nl.
checkword(train) :- write('Train found.'), nl.
评论
0赞
Philkav
11/23/2009
这个解决方案的唯一问题是,因为我的函数被反复调用,直到程序结束,当使用该函数并且我没有写“word1”时,它会说:“什么也没找到。例如,如果我输入:word1,我得到:'word1 contained',如果我输入:word2,我得到:'nothing found',如果我输入:word3,我得到:'nothing found',如果我再次输入word1,我得到:'word1 contained'。所以它只适用于第一个条件,之后所有其他条件似乎都被忽略了。
0赞
liori
11/23/2009
哈,我不知何故误读了你的代码......您想要查找所有匹配项。我会编辑我的答案。
4赞
Jerome
11/23/2009
#2
编写包含谓词的主要部分的标准方法是:
contains([word1|_]) :- !, write('word1 contained').
contains([word2|_]) :- !, write('word2 contained').
contains([word3|_]) :- !, write('word3 contained').
contains([Head|Tail]) :- contains(Tail).
这意味着:
- 当你找到一个单词时,不要再搜索了(这就是剪切 (!) 运算符的用途)。
- 当其他方法都不起作用时,在 Tail 上递归。
要添加答案以防万一找不到,只需在递归调用上添加另一个切口,以便仅在没有其他任何操作(包括递归)时调用后一种情况:
contains([word1|_]) :- !, write('word1 contained').
contains([word2|_]) :- !, write('word2 contained').
contains([word3|_]) :- !, write('word3 contained').
contains([Head|Tail]) :- contains(Tail), !.
contains(_) :- write('Nothing found').
评论
0赞
liori
11/23/2009
使用切口并不被认为是好的。
1赞
nedned
11/23/2009
好吧,使用cuts确实意味着你的程序不再是一个纯粹的逻辑程序,它们肯定会使阅读和调试代码变得痛苦,但谨慎使用,在适当的时候,它们可以产生更简洁和高效的代码。在编写效率高的大型代码库时,很难避免它们。
1赞
bcat
11/24/2009
另外,使用意味着程序无论如何都不是纯粹的逻辑。write
0赞
liori
11/24/2009
write
可能只是一个占位符。
1赞
nedned
11/25/2009
@liori,顺便说一句,prolog 中的 if-then 和 if-then-else 构造实际上是使用 cut 实现的。我只提到这一点,因为你似乎不赞成使用削减,但你的答案中有if-then-else。
1赞
nedned
11/23/2009
#3
以下是我的做法:
contains(Words) :-
findall(Word,has(Words,Word),Sols),
print_result(Sols).
% Word is a target word in the list Words
has(Words,Word) :-
member(Word,Words),
member(Word,[word1,word2,word3]).
print_result([]) :- write('Nothing found.\n').
print_result([X|Xs]) :- print_sols([X|Xs]).
print_sols([]).
print_sols([X|Xs]) :-
concat(X, ' contained.\n',Output),
write(Output),
print_sols(Xs).
这种方法的优点是它使用更高级别的抽象,使谓词更易于阅读。由于只有一个目标词列表,因此维护起来也变得更容易,而不必为每个新词添加单独的子句。
诀窍在于使用两次的谓词;一次从输入列表中选择一个项目,第二次测试它是否是目标词之一。使用此参数,然后生成在输入列表中找到的所有目标词。has
member/2
findall/3
注意:in 只是避免了在第一个子句中使用 cut。[X|Xs]
print_results
0赞
Kaarel
12/1/2009
#4
我认为 liori 有最好的答案。下面有一种略有不同的方法,在某些情况下可能有意义,即:
- 生成打印输出
- 如果打印输出为空,则打印“未找到”,否则输出打印输出
以下内容在 SWI-Prolog 中有效,在其他 Prolog 中可能无效,因为它使用:with_output_to/2
% Define what are the keywords
keyword(word1).
keyword(word2).
keyword(word3).
% Define how the found keywords are pretty-printed
print_keyword(W) :-
format("Found: ~w.~n", [W]).
% Generate a print-out and output it unless its empty
print_keywords(Words) :-
with_output_to(atom(PrintOut),
forall((member(W, Words), keyword(W)), print_keyword(W))),
(
PrintOut == ''
->
writeln('Nothing found.')
;
write(PrintOut)
).
上一个:Mercurial 和时间戳
评论