Prolog - 从基本案例中返回值

时间:2010-10-25 23:45:25

标签: prolog logic

好的,这是交易:

  • 我有两堆衬衫
  • 我想从每一堆中随机穿一件衬衫并把它们放在一堆新的
  • 然后获得新桩

以下是代码:

mix([],[],_).
mix(P1,P2, Pile):-
    takeshirt(P1,1,Taken1,Rem1), takeshirt(P2,1,Taken2,Rem2), #Take one
    append(Pile,Taken1,New),     append(New,Taken2,NewPile),  #Put both of them
    mix(Remain1,Remain2,NewPile).

这就是结果:

1 ?- mix([a,b],[c,d],NewPile).
NewPile = [] .

我希望它看起来像:

1 ?- mix([a,b],[c,d],NewPile).
NewPile = [b, d, a, c] .

或者无论结果如何。我通过图形化调试器进行了检查,发现当最后一次调用 mix 时,绑定是:

P1 = Taken1 = [b]
P2 = Taken2 = [c]
Pile           = [a, d]
Rem1 = Rem2 = []
New         = [a, d, b]
NewPile     = [a, d, b, c] #<--- Interresting  

所以当最终调用时,所需的值在NewPile中:

mix([],[],_). 

发生。在此之后,它像一个纸牌屋一样崩溃。

所以问题是:

mix([],[],_).

我想从基本情况返回_值,此规则 mix 实际上是从更高的实例中使用,我发送两个堆并获得新的堆。

更新
为了澄清关于 takehirt 规则的一些评论,这里是:

takeshirt(_,0,[],_).
takeshirt(List,Number,[Element|Taken],Remain) :- N > 0,
    length(List,Len),
    Index is random(Len) + 1,
    removeshirt_at(Element,List,Index,Remain),
    Number1 is Number - 1,
    takeshirt(Remain,Number1,Taken,Remain). 

1 个答案:

答案 0 :(得分:2)

请考虑对您的代码进行以下修改:

mix([], [], []) :- !.
mix(P1, P2, Pile) :-
    takeshirt(P1, 1, Taken1, Rem1), 
    takeshirt(P2, 1, Taken2, Rem2),
    append(Taken1, Taken2, Pile0),
    mix(Rem1, Rem2, Pile1),
    append(Pile0, Pile1, Pile).

似乎你需要累积'衬衫'(作为列表原子)。在这里,我们递归地将它们附加到mix/3Pile)的第三个参数上,直到两个输入列表都是空列表时命中基本情况(第一个子句)(注意切割{{这里需要1}},因为第二个子句的绑定模式与第一个匹配,所以我们要排除它)。第二个子句的行为,每个步骤从每个输入列表中取一件衬衫,要求它们的长度必须相等。

为了测试这一点,我使用了!的以下定义:

takeshirt/4

请注意,此处的第二个参数未使用,因为takeshirt(Ps, _, [P], Rem) :- select(P, Ps, Rem). 用于从列表中获取单个元素(衬衫),并返回余数。 select/3之后没有剪切(!)允许此谓词在从列表中选择所有其他元素(衬衫)时回溯。如果我们现在使用此定义执行您的示例查询,我们可以得到:

select

......我们可以看到1 ?- mix([a,b],[c,d],NewPile). NewPile = [a, c, b, d] ; NewPile = [a, d, b, c] ; NewPile = [b, c, a, d] ; NewPile = [b, d, a, c] ; false. 通过从第一堆(第一个输入列表)中取出一件衬衫,然后从第二堆(第二个输入列表)中取出一件衬衫,列举所有可能的“回合”。依此类推,直到两个输入列表都为空。如果您mix/3的定义没有留下选择点,(非回溯),那么您只能获得一个解决方案(如果有的话)。