【问题标题】:Satisfying a set of goals in Prolog在 Prolog 中满足一组目标
【发布时间】:2013-06-19 09:58:10
【问题描述】:

在 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).

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

  1. 接受一个模板、几个用于约束的参数(因此用于将模板的变量绑定到有用的值),以及一个用于指示它受哪个约束的变量。
  2. 生成要在此迭代中满足的约束,并将其应用于模板。
  3. 递归调用自身,以便满足剩余的约束。

我正在寻找的是一个类似于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] ;

【问题讨论】:

  • lambda.pl 可能会有所帮助,但我认为您应该选择 findall/3
  • lambda 没有随 SWI 分发有什么特别的原因吗?我很想能够use_module(library(lambda))
  • @DanielLyons:当我试图回答同样的问题时,我得到了一些反对意见 :)
  • 我可能对这个问题读得太多了,但它似乎涉及两个有趣的想法。一种是使用部分绑定的结构(您的模板)来表达单个约束的结果。另一个是包装器,它对参数化的约束序列执行for all量化,以串行方式应用于部分绑定的结构。
  • apply(G,[T]) 完全过时了!请改用call(G,T)

标签: prolog


【解决方案1】:

您在问题中描述的情况与您提供的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/1maplist/2 没有实质性不同,但我期待着发现我不会有一个有意义的实现。我错了。

【讨论】:

  • 我对 lambda.pl 不熟悉,而且迄今为止我也不知道 maplist,但 maplist 似乎让我完成了 80% 的工作。我将在今天晚些时候尝试并更新我的问题!
  • 我希望@hardmath 或其他人能找到一种方法来提出satisfyall/1,从而让您顺利完成剩下的工作。除了简单地依赖通常的统一过程之外,我没有看到使用模板结构的直接方法。希望其他人有更好的想象力,可以看到前进的方向。 :)
  • Animals 应该全局声明:maplist(Animals+\Animal^member(Animal, Animals), L)
【解决方案2】:

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

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

找到正确的方法来强制我们的规则沿着确定性路径工作可能很困难,可能是因为这不是 Prolog 应该的工作方式。

好吧,现在,停止哲学咆哮,让我们看看 Prolog 的大师们为我们提供了什么:SWI-Prolog 提供库(aggregate),您可以在其中找到 foreach,我认为它可以满足您的大部分需求之后:

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

研究如此复杂的内置函数可以为您的实现提供一些想法(使用控制台中的?- edit(foreach). 来检查源代码)。

请注意,它将生成器和目标分开,而在您的问题中,它们毫无希望地团结在一起。这当然是需要才能在生成器部分回溯。

顺便说一句,尝试了解文档页面中的小示例列表。 dif/2 过于复杂,但您确实需要掌握绑定的行为才能泛化您的递归谓词。

HTH

【讨论】:

  • 您好,抱歉回复晚了。我认为你让我走上了正确的道路。
猜你喜欢
  • 2023-04-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多