为什么';t Prolog为';狐狸、鹅和豆袋拼图';并显示';正确';相反

为什么';t Prolog为';狐狸、鹅和豆袋拼图';并显示';正确';相反,prolog,river-crossing-puzzle,Prolog,River Crossing Puzzle,我想用Prolog作为练习来解决“”问题 所以我写了 p(left). p(right). valid((Man, Goose, Fox, Beans)) :- p(Man),p(Goose),p(Fox),p(Beans), Man == Goose. valid((Man, Goose, Fox, Beans)) :- p(Man),p(Goose),p(Fox),p(Beans), Man == Beans, Goose \= Fox. valid((Man, Goose, Fox, Be

我想用Prolog作为练习来解决“”问题

所以我写了

p(left).
p(right).
valid((Man, Goose, Fox, Beans)) :- p(Man),p(Goose),p(Fox),p(Beans), Man == Goose.
valid((Man, Goose, Fox, Beans)) :- p(Man),p(Goose),p(Fox),p(Beans), Man == Beans, Goose \= Fox.
valid((Man, Goose, Fox, Beans)) :- p(Man),p(Goose),p(Fox),p(Beans), Man == Fox, Goose \= Beans.

step((Man1, Goose, Fox, Beans), "Man", (Man2, Goose, Fox, Beans)) :- 
    valid((Man1, Goose, Fox, Beans)), valid((Man2, Goose, Fox, Beans)),
    Man1 \= Man2.

step((Man1, Goose1, Fox, Beans), "Goose", (Man2, Goose2, Fox, Beans)) :- 
    valid((Man1, Goose1, Fox, Beans)), valid((Man2, Goose2, Fox, Beans)),
    Man1 \= Man2, Goose1 \= Goose2.

step((Man1, Goose, Fox1, Beans), "Fox", (Man2, Goose, Fox2, Beans)) :- 
    valid((Man1, Goose, Fox1, Beans)), valid((Man2, Goose, Fox2, Beans)),
    Man1 \= Man2, Fox1 \= Fox2.

step((Man1, Goose, Fox, Beans1), "Beans", (Man2, Goose, Fox, Beans1)) :- 
    valid((Man1, Goose, Fox, Beans1)), valid((Man2, Goose, Fox, Beans2)),
    Man1 \= Man2, Beans1 \= Beans2.

reachable(S, _,[], S).
reachable(S, Visited, [Step|Steps], Z) :- 
    step(S,Step,Tmp),valid(Tmp), not(member(Tmp,Visited)),
    reachable(Tmp, [Tmp|Visited], Steps, Z).

start((left,left,left,left)).
goal((right,right,right,right)).

solve(Steps) :- start(S), goal(Z), reachable(S, [], Steps, Z).
问题: 我想用
solve(X)。
我会得到一系列有效的步骤。但是相反,我得到了

?- solve(X).
false.
为什么我不能得到一个从开始到目标的步骤列表

代码解释
valid
检查4元组,其中第一个元素是“人”的位置,第二个元素是“鹅”的位置,第三个元素是“豆”的位置(如果没有人吃)

步骤(情景1,描述,情景2)
从一个有效情景到另一个有效情景的步骤


可访问(开始、情景列表、步骤、目标)
检查情景
目标
是否可以从情景
目标
中达到情景
目标,而情景列表
中的每个情景只访问一次,并且
步骤
描述了按哪个顺序采取了哪些步骤。

导致失败的主要问题是打字错误:

step((Man1, Goose, Fox, Beans1), "Beans", (Man2, Goose, Fox, Beans1)) :- 
应该是:

step((Man1, Goose, Fox, Beans1), 'Beans', (Man2, Goose, Fox, Beans2)) :- 
这将生成正确的解决方案。还有其他几个清理点:

  • 您在字符串上使用双引号,在Prolog中,它实际上是一个ASCII代码序列(Prolog列表)。如果您想要一个字符串仅仅显示为一个字符串,您可以使用单引号中包含的Prolog原子。因此,例如,
    “人”
    而不是
    “人”
  • reachable/2
    子句中的
    valid(Tmp)
    目标是多余的,因为
    步骤的先前目标确保
    Tmp
    将根据
    valid
    规则有效
  • 在这个特定的应用程序中,每个
    步骤/3
    子句中的两个
    有效的
    目标中的第一个是不必要的,因为您的第一个参数来自先前验证的步骤。(这取决于您希望
    步骤/3
    的“常规”表现)

除此之外,
solve/1
仍然会产生许多重复的结果,但它们都是正确的,并且所有正确的解决方案都被发现。

您是否尝试跟踪执行(使用目标
跟踪,求解(X)
)来检查代码失败的地方?@PauloMoura我不知道这一点。但是我刚刚试过,输出很长,我不完全理解。例如,什么是
p(右)?蠕变
平均值?我可以抑制这些吗?
爬行
只意味着它查询
p(右)
,然后它“爬行”到下一个目标(一次一个目标)。如果您继续执行这些步骤,它将通过代码一个目标一个目标地执行,您可以检查每个目标是否有意义。@mbrach:谢谢,就这样!目前,swipl向我显示ASCII值列表,而不是字符串。如何解决这个问题?用原子代替字符串:
'Man'
代替
“Man”
等等。Prolog中双引号中的字符串表示字符代码的ASCII序列(列表)。为了只获得一次所有可能的解,我想我可以使用
bagof(X,solve(T),L)。
其中
T
将是一个解决方案,不是?(我不知道
X
L
有什么好处)