1

我正在尝试制作序言功能。该函数读入一个句子,然后尝试提取一个关键词。如果找到关键字,它会打印一条消息。如果没有找到关键字,我希望它也打印一条消息。这是我的例子:

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

但显然这是错误的做法。

4

4 回答 4

4

编写contains谓词主要部分的标准方法是:

contains([word1|_]) :- !, write('word1 contained').
contains([word2|_]) :- !, write('word2 contained').
contains([word3|_]) :- !, write('word3 contained').
contains([Head|Tail]) :- contains(Tail).

意思是:

  • 当你找到一个单词时,不要再搜索了(这就是 cut (!) 运算符的用途)。
  • 当没有其他工作时,在尾部递归。

要在没有找到任何内容的情况下添加答案,只需在递归调用中添加另一个剪切,以便仅在没有其他方法(包括递归)起作用时才调用后一种情况:

contains([word1|_]) :- !, write('word1 contained').
contains([word2|_]) :- !, write('word2 contained').
contains([word3|_]) :- !, write('word3 contained').
contains([Head|Tail]) :- contains(Tail), !.
contains(_) :- write('Nothing found').
于 2009-11-23T12:22:53.053 回答
2

在命令式语言中,您会使用某种标志;例如:

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.
于 2009-11-23T12:09:17.170 回答
1

这是我将如何做到这一点:

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然后产生在输入列表中找到的所有目标词。

注意:[X|Xs]inprint_results只是避免在第一个子句中使用 cut。

于 2009-11-23T12:50:57.280 回答
0

我认为 liori 有最好的答案。这是一种在某些情况下可能有意义的稍微不同的方法,即:

  • 生成打印输出
  • 如果打印输出为空则打印“Nothing found”,否则输出打印输出

以下适用于 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)
    ).
于 2009-12-01T02:29:44.667 回答