2009-11-23 141 views
1

我試圖做一個序言功能。該函數讀入一個句子,然後嘗試提取一個關鍵詞。如果找到關鍵詞,則會打印一條消息。如果沒有找到關鍵字,我希望它也能打印一條消息。這裏是我的例子:SWI Prolog - 條件NOT?

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

標準的方式來寫你的的主要部分包含謂語是:

contains([word1|_]) :- !, write('word1 contained'). 
contains([word2|_]) :- !, write('word2 contained'). 
contains([word3|_]) :- !, write('word3 contained'). 
contains([Head|Tail]) :- contains(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 2009-11-23 15:04:16

+1

那麼,使用切割確實意味着你的程序不再是一個純粹的邏輯程序,它們當然可以使閱讀和調試代碼變得痛苦,但是在適當的時候使用它們可以產生更簡潔和高效的代碼。在編寫效率受到關注的大型代碼庫時,他們難以避免。 – nedned 2009-11-23 15:31:32

+1

另外,使用'write'意味着該程序不是完全合乎邏輯的。 – bcat 2009-11-24 00:35:07

2

在命令式語言中,你會使用某種標誌;例如:如果你想爲每個單詞(和抽象環路)不同的條款

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

,改變中間部分:

found = False 
for word in wordlist: 
    if word in ('car', 'train', 'plane'): 
     print "Found: " + word 
     found = True 
if not found: 
    print "Nothing found." 

您可以實現此標誌作爲另一個參數的條款於:

% 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

這個解決方案的唯一的問題是,因爲我的函數被反覆調用,直到節目結束,當使用的功能和我不寫'word1',它說:'找不到。'例如,如果我鍵入:word1,我得到:'word1包含'如果我鍵入:word2,我得到:'沒有發現'如果我鍵入:word3,我得到:'沒有發現'如果我再次鍵入word1,我得到: 'word1包含'。所以它只適用於第一個條件,所有其他人似乎被忽略之後.. – Philkav 2009-11-23 12:17:28

+0

哈,我不知何故誤讀你的代碼...你想找到所有的出現。我會編輯我的答案。 – liori 2009-11-23 15:05:11

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

這種方法的優點是,它使用更高的抽象水平,使得謂詞更容易閱讀。由於只有一個目標單詞列表,所以它也變得更容易維護,而不必爲每個新單詞添加單獨的子句。

訣竅是使用member/2兩次的has謂詞;一次從輸入列表中選擇一個項目,第二次測試它是目標單詞之一。將此用作findall/3的參數,然後生成在輸入列表中找到的所有目標單詞。

注意:[X|Xs]print_results只是避免必須在第一個條款中使用cut。

0

我認爲liori有最好的答案。這是一種稍微不同的方法,在某些情況下可能有意義,即:

  • 生成打印出
  • 如果打印出來是空的,然後打印「一無所獲」,否則輸出打印出

在SWI-Prolog的,可能下面的作品不能在其他Prologs因爲它使用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) 
    ).