4

我正在尝试在 Prolog 中实现 findall 谓词(是的,我知道它是内置的,这是用于分配的)。

它是这样写的:

my_findall(N,P,Pred,L) :- Pred, not(new(N,P)), !, assert(new(N,P)), my_findall(N1,P1,Pred,L1), L=[N,P,L1], retract(new(N,P)).
my_findall(_,_,_,  []).

出于某种原因,它只给了我第一个解决方案并停在那里,好像第二次调用 my_findall 失败了。据我了解,回溯机制应该遍历所有可能的选项,其中应该包括调用 Pred(N,P) 的所有选项,所以即使第二次调用在第一次尝试时应该失败(为 Pred 尝试的第一个选项已经被断言),它应该在放弃并转到 my_findall(( , ),_, []) 之前先尝试所有其他选项。

如果这不是它的工作原理,有没有办法在不完全重写解决方案的情况下强制这种行为?

4

1 回答 1

5

您的 Pred 包含未绑定的变量。在第一次迭代中调用 Pred 时,这些变量绑定到第一个可能的值。在您的递归步骤中,Pred 已经有绑定变量,它们不能更改值。所以......这个解决方案将不起作用。

来自 SWI-Prolog 的跟踪(由于某些原因,我不得不将 new/2 重命名为 item/2):

第一级(调用:my_findall(A,B,member(p(A,B), [p(1,2), p(3,4)]), L). )。

   Call: (7) my_findall(_G819, _G820, member(p(_G819, _G820), [p(1, 2), p(3, 4)]), _G840) ? creep
   Call: (8) lists:member(p(_G819, _G820), [p(1, 2), p(3, 4)]) ? creep
   Exit: (8) lists:member(p(1, 2), [p(1, 2), p(3, 4)]) ? creep

我们得到 p(A,B) = p(1,2)。此时 A 绑定为 1,B 绑定为 2。

^  Call: (8) not(item(1, 2)) ? creep
   Call: (9) item(1, 2) ? creep
   Fail: (9) item(1, 2) ? creep
^  Exit: (8) not(item(1, 2)) ? creep

好的,数据库中没有 item(1,2)。

^  Call: (8) assert(item(1, 2)) ? creep
^  Exit: (8) assert(item(1, 2)) ? creep

现在 item(1,2) 为真。递归调用:

   Call: (8) my_findall(_L215, _L216, member(p(1, 2), [p(1, 2), p(3, 4)]), _L199) ? creep

让我们用另一个解决方案做 Pred:

   Call: (9) lists:member(p(1, 2), [p(1, 2), p(3, 4)]) ? creep
                          ^^^^^^^

看到下划线的部分了吗?

要使这项技术发挥作用,您可能应该复制 Pred,递归地将 N 和 P 更改为新变量。对于每次迭代,您必须“创建”新的 N 和 P 对。检查 copy_term/2 ( http://www.swi-prolog.org/pldoc/doc_for?object=copy_term%2f2 )。

于 2009-07-08T15:20:41.410 回答