2017-05-05 74 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,但是我不能没有整个事情。 –