SWI Prolog - 有条件的不是?

SWI Prolog - conditional NOT?

提问人:Philkav 提问时间:11/23/2009 最后编辑:BenMorelPhilkav 更新时间:2/15/2014 访问量:2790

问:

我正在尝试制作一个 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).

但显然这是错误的做法。

Prolog 条件语句

评论


答:

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

这种方法的优点是它使用更高级别的抽象,使谓词更易于阅读。由于只有一个目标词列表,因此维护起来也变得更容易,而不必为每个新词添加单独的子句。

诀窍在于使用两次的谓词;一次从输入列表中选择一个项目,第二次测试它是否是目标词之一。使用此参数,然后生成在输入列表中找到的所有目标词。hasmember/2findall/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)
    ).