2017-05-05 72 views
1

我有我一直在說我寧願停留在一個序言家庭作業的問題。基本問題是我有三個列表,每個列表都有包含短語和值的成員。我應該隨機選擇列表中的一員,打印短語,重複,直到我從至少兩個列表,並打印短語的總價值印製詞組至少9Prolog的列表操作協助

什麼到目前爲止,我已經完成了數十次迭代,搜索了SWI文檔以及Lean Prolog Now和堆棧溢出。在這個過程中,我瞭解到使用assert()將項添加到列表會導致後續運行時出現錯誤,所有變量都是本地的,將單個項添加到空列表不會導致列表,但會產生結果在有趣的錯誤消息中進行下一次遞歸,並且不能改變變量的值。 我的程序成功執行以下操作:隨機選擇3個列表中的一個。隨機從該列表中抓取一個項目。將所選項目中的短語和值放入不同的變量中,並打印出來。

它的主要問題是,我的列表操作似乎並不奏效。行「append(Usedlist,Z,Usedlist3)」在代碼的第二次迭代中總是失敗,而不會引發錯誤。另外,我相信成員(Z,Usedlist)不能在第二次迭代中隨機獲得重複的情況下不能正常工作。但是,我懷疑錯誤是在附加內容中沒有正確添加到列表中。

相關代碼(濃縮到1名名單理智的原因)

main :- 
     A is 0, 
     B is 0, 
     C is 0, 
     Num is 0, 
     Usedlist = ["Garbage data1", "More garbage data"], 
     prologwhile(Num, A, B, C, Usedlist). 

    prologwhile(Num, A, B, C, Usedlist) :- Num < 9 , 
    F = [["time of day", 1], ["Season", 5], ["Yesterdays weather", 2], ["Month of last big storm", 2], ["Randomly generated riddle", 9], ["A captcha", 1], ['Current day', 6], ["Tomorrows date", 5]], 

    random_member(Rand, F), 
    nth0(1, Rand, Add), 
    writeln(Rand), 
    nth0(0, Rand, Z), 
    writeln(Usedlist), 
    ( member(Z, Usedlist) -> 
     writeln("Test5"), 
     prologwhile(Num, A, B, C, Usedlist) 
    ; writeln(Rand), 
     writeln(Add), 
     writeln("Test6"), 
     append(Usedlist, Z, Usedlist3), 
     writeln(Z), 
     C3 is 1, 
     Num3 is Num + Add, 
     writeln(Num3), 
     prologwhile(Num3, A, B, C3, Usedlist3) 
    ) 

樣本輸出

[Tomorrows date,5] 
[Garbage data1,More garbage data] 
[Tomorrows date,5] 
5 
Test6 
Tomorrows date 
5 
[time of day,1] 
[Garbage data1,More garbage data|Tomorrows date] 
[time of day,1] 
1 
Test6 
false. 

回答

3

讓我們解決它在階段

1,消除副作用並簡化程序

我開始與你的程序的下面直接的變化:

  1. 消除執行I/O的所有目標,因爲這些只能得到在推理的方式,我們希望執行有關程序。
  2. 代替X is C其中C恆定,變量只在代碼的其餘部分使用一次,我簡單地使用恆定  C直接

因此,我們得到:現在

 
main :- 
     prologwhile(0, 0, 0, 0, ["Garbage data1", "More garbage data"]). 

prologwhile(Num, A, B, C, Usedlist) :- 
     Num < 9 , 
     F = [["time of day", 1], ["Season", 5], ["Yesterdays weather", 2], ["Month of last big storm", 2], ["Randomly generated riddle", 9], ["A captcha", 1], ['Current day', 6], ["Tomorrows date", 5]], 
     random_member(Rand, F), 
     nth0(1, Rand, Add), 
     nth0(0, Rand, Z), 
     ( member(Z, Usedlist) -> 
      prologwhile(Num, A, B, C, Usedlist) 
     ; append(Usedlist, Z, Usedlist3), 
      Num3 is Num + Add, 
      prologwhile(Num3, A, B, 1, Usedlist3) 
     ). 

,爲便於閱讀,我提出以下額外變化:

  • 我分解出短語到自己的謂語
  • 我使用而不是列表代表Phrase-Value
  • 我使用模式匹配和一個更具有說服力的變量名稱。

總體而言,我們現在有:

 
main :- 
     prologwhile(0, 0, 0, 0, ["Garbage data1", "More garbage data"]). 

prologwhile(Num, A, B, C, Usedlist) :- 
     Num < 9 , 
     phrases(Ps), 
     random_member(Phrase-Value, Ps), 
     ( member(Phrase, Usedlist) -> 
      prologwhile(Num, A, B, C, Usedlist) 
     ; append(Usedlist, Phrase, Usedlist3), 
      Num3 is Num + Value, 
      prologwhile(Num3, A, B, 1, Usedlist3) 
     ). 

phrases(["time of day"-1, 
     "Season"-5, 
     "Yesterdays weather"-2, 
     "Month of last big storm"-2, 
     "Randomly generated riddle"-9, 
     "A captcha"-1, 
     'Current day'-6, 
     "Tomorrows date"-5]). 

從今往後,我認爲phrases/1在上面的代碼中定義的,並沒有隨身攜帶它的定義。

2.找到失敗的原因

現在實際推理有關程序啓動,因爲我們有:

 
?- main. 
false. 

爲了幫助您發現錯誤,我添加了以下定義該程序:

 
$(Goal) :- 
     portray_clause(Goal), 
     Goal. 

我們可以利用這個謂詞如下的prologwhile/5

 
prologwhile(Num, A, B, C, Usedlist) :- 
     Num < 9 , 
     phrases(Ps), 
     random_member(Phrase-Value, Ps), 
     ( member(Phrase, Usedlist) -> 
      prologwhile(Num, A, B, C, Usedlist) 
     ; $(append(Usedlist, Phrase, Usedlist3)), 
      Num3 is Num + Value, 
      prologwhile(Num3, A, B, 1, Usedlist3) 
     ). 

現在,我們得到:

 
?- main. 
append(["Garbage data1", "More garbage data"], "A captcha", _). 
append(["Garbage data1", "More garbage data"|"A captcha"], 'Current day', _). 
false. 

所以這裏的問題:

 
?- append(["Garbage data1", "More garbage data"|"A captcha"], 'Current day', _). 
false 

這個目標失敗因此整個計劃失敗。

你明明如下:

 
append(Usedlist, [Phrase], Usedlist3) 

即要追加合併兩個名單,的內容不是一個列表和一些其他。請注意,它往往是一個好主意,前置列表中的前元素,而不是追加他們獲得良好的性能,所以我們可以這樣寫:

 
Usedlist3 = [Phrase|Usedlist] 

所以,整個謂語變爲:

 
prologwhile(Num, A, B, C, Usedlist) :- 
     Num < 9 , 
     phrases(Ps), 
     random_member(Phrase-Value, Ps), 
     ( member(Phrase, Usedlist) -> 
      prologwhile(Num, A, B, C, Usedlist) 
     ; Usedlist3 = [Phrase|Usedlist], 
      Num3 is Num + Value, 
      prologwhile(Num3, A, B, 1, Usedlist3) 
     ). 

或更短:

 
prologwhile(Num, A, B, C, Usedlist) :- 
     Num < 9 , 
     phrases(Ps), 
     random_member(Phrase-Value, Ps), 
     ( member(Phrase, Usedlist) -> 
      prologwhile(Num, A, B, C, Usedlist) 
     ; Num3 is Num + Value, 
      prologwhile(Num3, A, B, 1, [Phrase|Usedlist]) 
     ). 

然而,我們還有:

 
?- main. 
false. 

爲了說明爲什麼這仍然失敗,想想案件中,謂詞應該 到舉行。一點反思之後,下面這樣一種情況,它應該保持:

 
prologwhile(Num, _, _, _, [_,_|_]) :- Num >= 9. 

因此,我們的情況下加入到我們的節目,獲得:

 
prologwhile(Num, _, _, _, [_,_|_]) :- Num >= 9. 
prologwhile(Num, A, B, C, Usedlist) :- 
     Num < 9 , 
     phrases(Ps), 
     random_member(Phrase-Value, Ps), 
     ( member(Phrase, Usedlist) -> 
      prologwhile(Num, A, B, C, Usedlist) 
     ; Num3 is Num + Value, 
      prologwhile(Num3, A, B, 1, [Phrase|Usedlist]) 
     ). 

注意如何我使用模式匹配在累積列表中檢測至少來自2個不同對的元素是 的情況。現在

,我們終於有:

 
?- main. 
true . 

這很酷,但不是有用。

3.報告頂層

實際的解決方案現在,是時候詢問有關程序的一些更深層的問題。例如,這些論據真的在這裏做了什麼?讓我們完全忘記ABC,獲得:

 
main :- 
     prologwhile(0, ["Garbage data1", "More garbage data"]). 

prologwhile(Num, [_,_|_]) :- Num >= 9. 
prologwhile(Num, Usedlist) :- 
     Num < 9 , 
     phrases(Ps), 
     random_member(Phrase-Value, Ps), 
     ( member(Phrase, Usedlist) -> 
      prologwhile(Num, Usedlist) 
     ; Num3 is Num + Value, 
      prologwhile(Num3, [Phrase|Usedlist]) 
     ). 

它的工作原理一樣好:

 
?- main. 
true . 

在另一方面,一個重要的事情似乎缺少,即短語我們實際上有 使用!讓我們通過介紹來表示它們。在這種情況下,一個有用的命名規則是一對Ls0Ls,與Ls0表示的初始列表,Ls我們想要的頂層報告的最後一個。

我們也可以簡單地忘記「垃圾」元素。

因此,整個程序就變成了:

 
solution(List) :- 
     prologwhile(0, [], List). 

prologwhile(Num, Ls, Ls) :- Num >= 9, Ls = [_,_|_]. 
prologwhile(Num, Ls0, Ls) :- 
     Num < 9 , 
     phrases(Ps), 
     random_member(Phrase-Value, Ps), 
     ( member(Phrase, Ls0) -> 
      prologwhile(Num, Ls0, Ls) 
     ; Num3 is Num + Value, 
      prologwhile(Num3, [Phrase|Ls0], Ls) 
     ). 

phrases(["time of day"-1, 
     "Season"-5, 
     "Yesterdays weather"-2, 
     "Month of last big storm"-2, 
     "Randomly generated riddle"-9, 
     "A captcha"-1, 
     'Current day'-6, 
     "Tomorrows date"-5]). 

樣品查詢:

 
?- solution(Ls). 
Ls = ["Randomly generated riddle", "Season"] ; 
false. 

您也可以輕鬆擴展這個報告的總價值,無論是在構建列表或一個簡單的額外步驟。我把這留作練習。還請注意prologwhileisnotaseasytoread,例如using_underscores_would_be

+1

感謝您的幫助。這似乎工作。 –

+1

@mat:非常好的指導性答案,+ s(0)! – tas

+0

由於我一直在研究這個問題,你能否向我解釋最後兩步如何工作?我需要重新加入A,B和C,但是我不能沒有整個事情。 –