我一直在练习Prolog。我要编写的函数是compose(L1,L2,L3)。它由按顺序交错的L1和L2元素组成,直到其中一个变为nil,然后在末尾附加该非nil列表。当给定L1和L2作为输入时,该函数运行良好(即,打印出正确的L3),但是当我输入L3并尝试获取所有逻辑上可能的输入L1和L2时,我遇到了“栈外”错误。例如,对于以下功能代码,
compose([],[],[]).
compose(L1,[],L3):-
append(L1,[],L3).
compose([],L2,L3):-
append([],L2,L3).
compose([H1|T1],[H2|T2],L3):-
compose(T1,T2,Tail),
append([H1],[H2],Head),
append(Head,Tail,L3).
?-compose(L1,L2,[a,b,c]).
会给我出栈错误。我该如何解决这个问题?
答案 0 :(得分:2)
我应该如何解决这个问题?
首先,尝试了解为什么查询不会终止。您可以尝试想象Prolog如何进行,但是要警告,这可能变得非常复杂。毕竟,Prolog结合了两个控制流(“与”和“或”控制),并且具有部分未知的数据,这些数据在更传统的语言(OO和FP)中不存在。因此,我不想模仿Prolog,而是让Prolog帮助我定位错误。为此,我在程序中添加了尽可能多的目标 false
,以使查询仍然不会终止。这是最大值,称为failure-slice:
compose([],[],[]) :- false.compose(L1,[],L3):- false,append(L1,[],L3).compose([],L2,L3):- false,append([],L2,L3). compose([H1|T1],[H2|T2],L3):- compose(T1,T2,Tail), false,append([H1],[H2],Head),append(Head,Tail,L3). ?- compose(L1, L2, [a,b,c]), false.
我们可以跳过您的第一条款。仅关注最后一条规则的第一个目标!因此,无非是:
compose([H1|T1],[H2|T2],L3):- compose(T1,T2,Tail), false, ... . ?- compose(L1, L2, [a,b,c]), false.
在这个小小的程序中,compose/3
的第三个参数被完全忽略。没有人想要L3
。因此,L3
对终止没有影响。为了使此终止,我们需要在目标之前以某种方式约束L3
。 other answer向您展示如何操作。
(此方法适用于纯Prolog程序的任何非终止问题,请参见failure-slice。)
答案 1 :(得分:1)
首先将其重新编写为更简单但完全等效的
compose([],[],[]). % some redundancy here
compose(L1,[],L1).
compose([],L2,L2). /*
compose([H1|T1],[H2|T2],L3):- % whole solution
compose(T1,T2,Tail),
Head = [H1,H2],
L3 = [Head|Tail]. */
现在可以清楚地知道问题在于递归, first 首先计算其余结果(Tail
),然后只有 then 完成(为L3
)。
相反,将其扭曲
compose([H1|T1],[H2|T2],[H1,H2|Tail]):- % single step
compose(T1,T2,Tail).
所以现在我们有了 co </ em>递归,并且有效率。它首先创建结果的(肯定是有限的)起始部分,然后填充缺失的部分。
(在上面,“创建”可以与“消费”互换,就像Prolog的双向特性一样。单步,它并不关心消耗了哪些参数,以及产生的。)
答案 2 :(得分:0)
“超出全局堆栈”错误通常意味着您的递归陷入了无限循环,因此不断调用谓词,直到堆栈用尽。
这里进入无限循环的原因是因为它将首先发出结果:
?- compose(L1,L2,[a,b,c]).
L1 = [a, b, c],
L2 = [] ;
L1 = [],
L2 = [a, b, c] ;
L1 = [a, c],
L2 = [b] ;
但随后它将寻找更多解决方案。这样做是根据您的程序,每次都用[H1|T1]
统一第一项,而用[H2|T2]
统一第二项。然后,您立即进行递归调用,因此Prolog无法检查H1
和H2
是否实际上是第三个参数中的前两个元素。
但是,我们首先不需要所有这些append/3
调用,等等。我们可以对参数执行简单的统一操作:
compose([],L2,L2).
compose(L1,[],L1).
compose([H1|T1],[H2|T2],[H1, H2|T3]) :-
compose(T1,T2,T3).
对于给定的查询,这将在建议的解决方案之后终止:
?- compose(L1,L2,[a,b,c]).
L1 = [],
L2 = [a, b, c] ;
L1 = [a, b, c],
L2 = [] ;
L1 = [a],
L2 = [b, c] ;
L1 = [a, c],
L2 = [b] ;
false.
实际上,由于递归,最终第三个参数将是一个空列表,因此无法与[H1, H2|T3]
列表统一。