在Prolog中满足一系列目标

时间:2013-06-19 09:58:11

标签: prolog

在Prolog中,我经常通过提供模板(包含变量的结构)然后满足一组约束来解决问题。一个简单的例子可能是:

go(T) :-
    T = [_, _, _],
    member(cat, T),
    member(dog, T),
    member(mouse, T).

实际上,约束集是以其他方式生成而不是被修复,我必须编写一个递归谓词来依次满足每个约束:

go(T) :-
    T = [_, _, _],
    findall(A, animal(A), As),
    % satisy member(A, T) for each A in As
    fill_in_animals(T, As)

fill_in_animals(T, []).
fill_in_animals(T, [A|Rest]) :-
    member(A, T),
    fill_in_animals(T, Rest).

请注意,我的问题不是与列表相关的约束,甚至参数也不能总是很容易地生成为要传递给上面使用的相对简单的辅助谓词的列表。在实践中,我发现帮助器是我每次写的一个相当笨拙的谓词,其中:

  1. 接受一个模板,几个用于约束的参数(因此用于将模板的变量绑定到有用的值),以及一个用于指示它最多的约束的变量。
  2. 生成约束以在此迭代中满足,将其应用于模板。
  3. 递归调用自身,以便可以满足剩余的约束。
  4. 我正在寻找的是findall之类的谓词,它将一个接一个地满足一系列目标。类似的东西:

    % satisfyall(:Goal)
    % backtracks on Goal but keeps all bindings from each fully satisfied goal.
    
    satisfyall((animal(A), member(A, T)))
    

    我正在寻找的答案不一定是这种形式。实际上,回溯目标与维护由此产生的每组绑定之间可能存在矛盾。

    我希望我已经解释了我的问题,以便能够帮助我们。 (如果不让我知道的话。)为这个冗长的问题提前道歉!

    更新(2年后)

      

    我今天晚些时候会试一试并更新我的问题!

    请注意,我从未说过我会在尝试的同一天更新问题。 ;-)

    @CapelliC引导我走向正确的方向,我发现了一种似乎运作良好的模式:

    ?- Gs = [member(red),member(blue)], T = [_,_], foreach(member(G, Gs), call(G, T)).
    T = [red, blue] ;
    T = [blue, red] ;
    

2 个答案:

答案 0 :(得分:3)

您在问题中描述的情况与您提供的satisfyall/1谓词的签名略有不同。 fill_in_animals示例中没有涉及回溯,至少不涉及流出go/1的变量。对子目标的满意度可能存在“小回溯”,但总体目标不会失败,同时保持绑定不变。

一个陈腐的,可能无益的解决方案是使用maplist/2。例如,您的示例很容易实现:

?- length(L, 3), maplist(animal, L).
L = [cat, cat, cat] ;
L = [cat, cat, dog] ;
L = [cat, cat, mouse] ;
L = [cat, dog, cat] ;
...
L = [mouse, mouse, dog] ;
L = [mouse, mouse, mouse].

您可以通过添加一个谓词来继续使用物化数据库:

% just flips the arguments of member/2
memberof(L, A) :- member(A, L).

然后我们可以使用findall/3来完成工作:

?- findall(A, animal(A), Animals), 
   length(L, 3), 
   maplist(memberof(Animals), L).

Animals = [cat, dog, mouse],
L = [cat, cat, cat] ;
Animals = [cat, dog, mouse],
L = [cat, cat, dog] ;
Animals = [cat, dog, mouse],
L = [cat, cat, mouse] ;
...
Animals = [cat, dog, mouse],
L = [mouse, mouse, dog] ;
Animals = [cat, dog, mouse],
L = [mouse, mouse, mouse].

这应该清楚为什么lambda.pl会有所帮助。你不需要帮助器谓词,你可以简单地写:

?- findall(A, animal(A), Animals), 
   length(L, 3), 
   maplist(\Animal^member(Animal, Animals), L).

(未测试的)

如果您真的想要绕过变量绑定和取消绑定,我认为您将为自己创建一个调试噩梦,但SWI-Prolog有一个global variable facility您可以使用。我模糊地回忆起在asserta / retract不足以完成这项任务的地方。

我想的越多,对我的感觉越多,就像satisfyall/1的{​​{1}}没有实质性地与maplist/2有所不同,但我很期待发现我错了。

答案 1 :(得分:1)

您肯定知道回溯将需要撤消对工作所做的更改。 这是Prolog算法的核心,也是Prolog能力的源泉。

当涉及到更常见的计算时,这也是一个弱点,就像必然涉及副作用或循环一样。

找到正确的方法迫使我们的规则在确定性路径上工作可能很困难,可能是因为这不是Prolog的工作方式。

嗯,现在,停止哲学咆哮,让我们看看Prolog的大师们为我们提供了什么:SWI-Prolog提供了图书馆(aggregate),你找到了foreach,我认为你在追求什么:

?- foreach(animal(X), member(X,L)).
L = [cat, dog, mouse|_G10698] .

研究这样复杂的内置函数可以让您对实现有所了解(从控制台使用?- edit(foreach).来检查源代码。)

请注意,它将发电机和目标分开,而在你的问题中它们是无望的联合。那当然需要才能在生成器部分上仅回溯

BTW,尝试了解doc页面中的小样本列表。它由dif / 2过于复杂,但你真的需要掌握绑定的行为才能推广你的递归谓词。

HTH