我刚开始学习Prolog。其中一个练习要求我向后写/ 2,当第二个列表参数与第一个列表参数相反时,谓词为真。我必须使用粘贴谓词来向后写。
paste([], L, L).
paste([X|L1], L2, [X|L3]) :- paste(L1,L2,L3).
我已经工作了好几个小时但仍然没有得到答案。
以下是我所拥有的:
backwards([H|T], Lrev) :- paste([X|T], X, Lrev).
backwards([],[]).
答案 0 :(得分:3)
Prolog列表是一种非常简单的数据结构。
[]
表示。'.'/2
表示,其中第一个参数是列表&#39; s head ,第二个参数是列表&#39; s < em> tail ,另一个列表,空或非空。Prolog列表符号在该数据结构上是语法糖:
[]
是两个符号中的空列表。对于非空列表,
[a]
是结构'.'(a,[])
。[a,b]
是结构'.'(a,'.'(b,[]))
[a,b,c]
是结构'.'(a,'.'(b,'.'(c,[])))
[Head|Tail]
完全等同于'.'(Head,Tail)
。[a|[b,c]]
完全等同于[a,b,c]
和'.'(a,'.'(b,'.'(c,[])))
很容易理解为什么人们可能更喜欢一点语法糖。
然后,列表的结构就像更常见的...常规语言中的经典单链表一样,使得从现有列表的头部(左侧)末端添加和删除项目变得微不足道。
需要注意的是,如果一次一个从一个列表中移除项目并将项目添加到另一个列表中,则会生成 reverse <中的第一个列表/ em>订单。
一个常见的Prolog习语是使用 helper 或 accumulator 参数,该参数通过递归来携带状态。通常这个助手将以初始值播种。还可以注意到,大多数递归问题都有一个或两个特殊情况和更广泛的一般情况。
这样一个反转列表的谓词可能看起来像这样:
paste( [] , Rs , Rs ) . % Special Case: if the source list is empty, unify the accumulator with the result.
paste( [X|Xs] , T , Rs ) :- % General Case:
T1 = [X|T] , % - prepend the current list head to the temporary, creating a new temporary, and
paste(Xs,T1,Rs) % - recurse down.
.
有人可能会注意到,为简单起见,可以重构上面的第二个子句,以删除新临时的显式创建:
paste( [] , Rs , Rs ) . % Special Case: if the source list is empty, unify the accumulator with the result.
paste( [X|Xs] , T , Rs ) :- % General Case:
paste(Xs,[X|T],Rs) % - prepend the current head to the temporary and recurse down.
.
无论哪种方式,此谓词都要求临时使用初始空列表([]
)播种。
如此称呼它,然后:
paste( [a,b,c] , [] , R ).
产生
R = [c,b,a]
获得paste/3
谓词后,创建backwards/2
谓词本身就是简单的:
backwards( L , R ) :- paste( L , [] , R ) .
这揭示了另一种常见的Prolog习语:一个琐碎的公共&#34;谓词调用&#34;私有&#34;实际完成所有工作的 worker predicate 。