坚持Prolog

时间:2014-10-08 16:49:50

标签: recursion prolog

我刚开始学习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([],[]).

1 个答案:

答案 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