成员在Prolog中断言

时间:2009-11-24 01:55:32

标签: prolog

我想定义一个成员谓词。

成员(A,B)表示列表A的所有成员都是列表B的成员。 top(N)定义A可以有多长。

这是我的尝试:

top(5).

members([X], L):-
   member(X, L).
members([X| Xs], L):-
   member(X, L),
   members(Xs, L),
   length(Xs, M),
   top(N),
   M < N.

我想用它如下:

members(L, [1,2,3]).

我的实施问题是,如果我;为了得到新的答案,我将以错误结束:超出本地堆栈

?- members(I, [1,2,3]).
I = [1] ;
I = [2] ;
I = [3] ;
I = [1, 1] ;
I = [1, 2] ;
I = [1, 3] ;
I = [1, 1, 1] ;
I = [1, 1, 2] ;
I = [1, 1, 3] ;
I = [1, 1, 1, 1] ;
I = [1, 1, 1, 2] ;
I = [1, 1, 1, 3] ;
I = [1, 1, 1, 1, 1] ;
I = [1, 1, 1, 1, 2] ;
I = [1, 1, 1, 1, 3] ;
;ERROR: Out of local stack

如何更改代码以防止内存不足?

4 个答案:

答案 0 :(得分:5)

如前所述,您的问题是在递归调用之后进行长度检查,这意味着递归是无限制的。不幸的是,只需将长度检查移动到递归调用之上就可以了......

members([X], L):-
   member(X, L).
members([X|Xs], L):-
    length(Xs, M),
    top(N), M < N,
    member(X, L),
    members(Xs, L).

......不是很好,我们得到这个:

L = [3, 1, 2, 3, 3] ;
L = [3, 2, 2, 3, 3] ;
L = [3, 3, 2, 3, 3] ;
L = [3, 1, 3, 3, 3] ;
L = [3, 2, 3, 3, 3] ;
L = [3, 3, 3, 3, 3] ;
ERROR: Out of global stack

虽然这给了我们答案,但它不是那么有用,因为它不能放入更大的谓词中,因为它会中断。它打破了,因为我们只是进一步推动了这个问题。原因如下:

问题是您是以自上而下的方式构建列表。换句话说,我们定义这样的列表:List = [Head|Tail]我们在头部和状态上规定了一些约束,即Tail由一组由相同约束定义的元素组成,并由一个基本案例限定。这意味着当我们处于递归调用的中间时,我们实际上只能访问Head - 我们无法访问Tail的内容,因为只有在解释器完全消失后才会构建它 down 并达到基本情况(即members([X], L) :-),然后连续将每个尾部添加到其头部,直到最终列表构建完毕。

看起来我们可以访问长度,因为length/2调用位于递归谓词的中间,但是因为传递到列表的length/2的变量是这个阶段没有任何约束,Prolog会等到它在计算长度之前完成此点之下的递归调用。问题当然是长度检查是递归的边界,所以它会一直持续到内存不足为止。

虽然自上而下递归往往是构造Prolog谓词的默认方式,但正如此示例所示,有时我们需要访问我们正在创建的数据结构。解决方案是使用自下而上的递归。这是通过累加器谓词在Prolog中实现的,累加器谓词以空列表开始,然后通过传递累加器列表(完全接地列表)逐个构建列表 up 递归谓词。以下是我为此问题编写累加器谓词的方法:

members(I,L) :-
    members_accumulator([],I,L).

members_accumulator(Tail,I,L) :-
    length(Tail, M),
    top(N),
    M < N,
    member(X, L),
    members_accumulator([X|Tail],I,L).
members_accumulator(I,I,_).

我们需要两个谓词,因为第一个是累加器的包装器,它将空列表传递给累加器谓词。基本情况不再与空列表有任何关系 - 所有它必须做的是声明最终的累加器列表实际上是我们所追求的最终列表(为此目的已经穿过累加器谓词) 。此外,在这种情况下,累加器谓词需要按此顺序,否则将有一个选择点在结束时评估为错误。

让人们在Prolog中进行递归,当你需要使用自下而上的递归而不是自上而下是一个非常重要的壮举。在我对The Art of Prolog进行了很好的阅读之前,我根本没有完全掌握它。还应该有很多关于累积器在线的信息。

答案 1 :(得分:4)

这是一个替代实现,它不需要计算列表的长度。这里N是列表A的长度。这个解决方案提供所有答案而不会出现堆栈。

members([X],L,1) :- member(X,L).
members([H|T],L,N) :- N>1 , member(H,L) , N1 is N-1, members(T,L,N1).

执行示例:

?- members(L,[1,2,3],5).
L = [1, 1, 1, 1, 1] ;
L = [1, 1, 1, 1, 2] ;
L = [1, 1, 1, 1, 3] ;
L = [1, 1, 1, 2, 1] ;
...
L = [3, 3, 3, 1, 2] ;
L = [3, 3, 3, 3, 1] ;
L = [3, 3, 3, 3, 2] ;
L = [3, 3, 3, 3, 3] ;
No

答案 2 :(得分:1)

您在递归后检查深度。因此,递归的深度不受限制,只有结果列表被丢弃的时间太长。

答案 3 :(得分:1)

使用Test at eval.in maplist/2和成员谓词lambdas,只需写下:

:- use_module(library(lambda)).

members(As,Bs,N) :-
   length(Xs,N),
   append(As,_,Xs),
   maplist(Bs+\A^memberd(A,Bs), As).

带缩写答案序列的示例查询:

?- members(As,[1,2,3],5).
As = [         ] ;
As = [        1] ; As = [        2] ;           As = [        3] ;
As = [      1,1] ; As = [      1,2] ; /* ... */ As = [      3,3] ;
As = [    1,1,1] ; As = [    1,1,2] ; /* ... */ As = [    3,3,3] ;
As = [  1,1,1,1] ; As = [  1,1,1,2] ; /* ... */ As = [  3,3,3,3] ;
As = [1,1,1,1,1] ; As = [1,1,1,1,2] ; /* ... */ As = [3,3,3,3,3] ;
false.

以上查询普遍终止。 让我们看一下解决方案集的大小:

?- setof(As,members(As,[1,2,3],5),Ass), length(Ass,N_Solutions).
Ass         = [[],[1],[1,1],[1,1,1],[1,1,1|...],[1,1|...],[1|...],[...|...]|...],
N_Solutions = 364.

?- 364 =:= 1 + 3 + 3^2 + 3^3 + 3^4 + 3^5.
true.