我已经编写了以下用于撤销给定列表的小知识库,
reverse_list([],[]).
reverse_list([X|L1], [L2|X]) :-
reverse_list(L1, L2).
目前,执行时
reverse_list([a,b,c,d], X).
它产生,
X = [[[[[]|d]|c]|b]|a].
对其发生原因的解释将非常感激。而且,我该如何解决这个问题呢?
我认为在最后一步L2成为[]并且在反向传播期间它变得像那样。
如何使用此方法的修改获得类似X = [d,c,b,a]
的输出。
答案 0 :(得分:5)
您可以推理代数关于Prolog程序。
为了向您展示一种方式,让我们考虑一个更简单的目标,该目标也会显示问题:
?- reverse_list([a], Ls). Ls = [[]|a].
为什么这样做?
因为,如果以下, 。
?- [a] = [X|L1], Ls = [L2|X], reverse_list(L1, L2). L1 = L2, L2 = [], Ls = [[]|a], X = a.
我只是插入reverse_list/2
的定义,使等效与原始查询相关,因此当然仍然显示问题。
现在重点:我们可以概括查询(只是省略一些目标),仍然看到问题:
?- [a] = [X|L1], Ls = [L2|X]. L1 = [], Ls = [L2|a], X = a.
很明显子句头包含错误:您正在描述[Ls|a]
形式的术语。这是不是列表,因为a
是 atom 。
+1以获得良好的命名约定! list_reversed/2
会更好(避免强制要求其他使用模式也有意义),并且可能会有更好的名称。
答案 1 :(得分:5)
您发现您的程序存在问题,而@mat向您展示了为什么给出的答案不正确。这是另一种本地化问题的方法:简单地坚持你的期望!在您的情况下,这将是:
?- reverse_list([a,b,c,d], [d,c,b,a]).
false. % unexpected failure
所以在这里你说这应该是这种情况,但事实并非如此。因此,您的程序太专业于查询。它缺乏这种解决方案。
要在程序中找到负责任的部分,您现在可以概括您的程序以使目标仍然失败。
:- op(950, fy, *). % prefix *, see this for more. *(_). reverse_list([],_/*[]*/). reverse_list([X|L1], [L2|X]) :- *reverse_list(L1, L2).
所以我已经删除了规则中的目标和事实中的第二个参数 - 但程序仍然失败了!您现在需要修改剩余可见部分中的某些内容以解决问题。
reverse_list([X|L1], [L2|X])
^ ^
X
作为元素和列表出现一次。
有一个简单的约定可以避免此类错误:名称列表最好添加复数 - s
。因此,如果您有一个元素X
,请调用列表Xs
,这将是[X|Xs]
。通过这种方式,问题将更容易发现:
reverse_list([X|Xs], [Ys|X]) :- ...
^ suspicious!
答案 2 :(得分:4)
要反转列表,您需要三个参数:
reverse_list(L,L1):-reverse_list(L,[],L1).
reverse_list([],ACC,ACC).
reverse_list([X|L], ACC,L1):- reverse_list(L,[X|ACC],L1).
首先调用reverse_list(L,L1)
调用reverse_list(L,[],L1).
,其中第二个参数用作累加器,以与第一个列表相反的顺序存储所有元素。
所以它调用reverse_list([X|L1], ACC,L).
,在第一次调用ACC(累加器)是空列表,然后你递归调用reverse_list(L1,[X|ACC],L).
这是同样的事情,但你从第一个列表移动X并存储到累加器。
最后,当第一个列表为空并且所有元素以相反顺序传递给ACC时,该过程停止,然后您将第三个列表与ACC统一。
举个例子:
L=[1,2,3],ACC=[],L
未被评估。
致电reverse_list([2,3],[1],L).
在下一个递归步骤中调用reverse_list([3],[2,1],L)。
致电reverse_list([],[3,2,1],[3,2,1]).
最后一个列表与ACC=[3,2,1]
统一。
查询上面的例子给出:
?- reverse_list([1,2,3],L).
L = [3, 2, 1].