Prolog中的zip功能

时间:2011-11-30 05:13:29

标签: list prolog

我是Prolog的新手,我的任务要求我们实现如下所述的功能:

编写一个Prolog谓词zip(L1,L2,L3)如果列表L3是通过压缩(即混洗“或交错”)列表L1和{{1 }}。更新:列表L2L1可以有不同的长度。例如,完成后,您应该得到以下行为:

L2

我正在考虑创建一个包含?- zip([1,2],[a,b],[1,a,2,b]). true. ?- zip([1,2],[a,b],X). X = [1, 2, a, b] ; X = [1, 2, a, b] ; X = [1, a, 2, b] ; X = [1, a, b, 2] ; X = [a, 1, 2, b] ; X = [a, 1, b, 2] ; X = [a, b, 1, 2] ; X = [a, b, 1, 2] ; false. ?- zip([1,2],[a,b],[1,2,a,b]). true. ?- zip(X,[a,b],[1,a,2,b]). X = [1,2] true. ?- zip([1,2],X,[1,a,2,b]). X = [a,b] true. L1元素的列表,然后将列表与L2进行比较。但是我不熟悉Prolog中的语法和循环。

3 个答案:

答案 0 :(得分:7)

实际上,我有三个答案。你可能想要第三个。但无论如何都要经历其他人。

1个不同的问题

我不确定你想要你所描述的关系。你是 同时学习OCaml和Python以及那些语言的zip意味着 别的。它意味着:

zip([], [], []).
zip([], [_|_], []).
zip([_|_], [], []).
zip([X|Xs], [Y|Ys], [X-Y|XYs]) :-
   zip(Xs, Ys, XYs).

?- zip([1,2],[3,4],XYs).
XYs = [1-3,2-4].

请注意Prolog中的不同惯例。虽然OCaml,Python以及Haskell使用(X,Y)来表示元组,但Prolog中的常见惯例是使用(X-Y)。这里的减号并不意味着减法。这只是一个未被解释的术语。

在Prolog中实现此功能的常用方法是use maplistmaplist要求所有列表的长度相同。

2隔行扫描

(编辑)另一种解释如下。名称为interlace/3shuffle/3 在这里会很理想。最近@salva向我们展示了very beautiful solution 这个。别忘了给它+1!让我只展示一些很酷的方式 可以使用它:

?- shuffle([1,2],[3,4],Zs).
Zs = [1,3,2,4].

你已经知道了。但为什么我们需要同时给出两个列表[1,2][3,4]到Prolog?这不是一种简单的编程语言 迫使你告诉一切。如果你懒得输入复杂的列表或其他术语,只需输入一个变量,看看Prolog是如何计算出来的。所以,让我们用a替换第二个列表 变量

?- shuffle([1,2],Ys,Zs).
Ys = [],
Zs = [1,2] ;
Ys = [_G607],
Zs = [1,_G607,2] ;
Ys = [_G607,_G616|_G617],
Zs = [1,_G607,2,_G616|_G617].

我们以这种方式问:YsZs如何看起来像shuffle / 3是真的?事实上,Ys有3个答案:

  • []是空列表。然后Zs[1,2]。所以这是一个解决方案。

  • [_G607]是一个只包含一个元素的列表。 Zs[1,_G607,2]_G607是一个自由变量。它可能有一个更好的名称,但关键是这个变量在YsZs内发生 。这个答案说:适合该变量的所有术语都是解决方案。所以我们在这里有无限多个解决方案,只需一个答案。

  • [_G607,_G616|_G617]表示包含至少两个元素的列表。

这是一个更酷的查询:

?- shuffle(Xs,Xs,Zs).
Xs = Zs, Zs = [] ;
Xs = [_G592],
Zs = [_G592,_G592] ;
Xs = [_G592,_G601],
Zs = [_G592,_G592,_G601,_G601] ;
Xs = [_G592,_G601,_G610],
Zs = [_G592,_G592,_G601,_G601,_G610,_G610]
...

看看Zs中是否存在同一变量的重复项!

3交织

也许这就是你真正想要的东西:

intertwine([], [], []).
intertwine([E|Es], Fs, [E|Gs]) :-
   intertwine(Es, Fs, Gs).
intertwine(Es, [F|Fs], [F|Gs]) :-
   intertwine(Es, Fs, Gs).

在下面的查询中,我们询问可以交织在一起的列表,以便给出三元素列表作为结果!

?- length(Zs,3), intertwine(Xs,Ys,Zs).
Zs = Xs, Xs = [_G648,_G651,_G654],
Ys = [] ;
Zs = [_G648,_G651,_G654],
Xs = [_G648,_G651],
Ys = [_G654] ;
Zs = [_G648,_G651,_G654],
Xs = [_G648,_G654],
Ys = [_G651] ;
Zs = [_G648,_G651,_G654],
Xs = [_G648],
Ys = [_G651,_G654] ;
Zs = [_G648,_G651,_G654],
Xs = [_G651,_G654],
Ys = [_G648] ;
Zs = [_G648,_G651,_G654],
Xs = [_G651],
Ys = [_G648,_G654] ;
Zs = [_G648,_G651,_G654],
Xs = [_G654],
Ys = [_G648,_G651] ;
Zs = [_G648,_G651,_G654],
Xs = [],
Ys = [_G648,_G651,_G654].

答案 1 :(得分:3)

您的程序需要以某种方式迭代列表以将L1和L2交错为L3或将L3拆分为L1和L2。当你在帖子中写“循环”时,你的意思是这个迭代。在Prolog中,您使用递归谓词来实现类似循环的行为。每个递归谓词都需要一些锚点。在你的程序中,其中一个锚可能看起来像这样:

zip([], L, L) :- !.

即,当L1是空列表时,L3将仅由L2组成。

注意条款末尾的剪切(!/0)。它告诉Prolog,一旦谓词调用成功地与该子句的头部统一,它就不需要寻找替代方案。这是为了避免不必要的选择点。

这一个锚是不够的。其余的应该不难找到。

现在您已经拥有了锚点,您可以考虑递归。行为应该是这样的:L3的下一个元素(“头”)可以是L1的下一个元素或L2的下一个元素(即,这里有一个选择,因此是“选择点”)。每个案例都可能属于自己的条款。

无论选择什么,然后再次使用L1(或L2)的剩余部分(“尾部”)和L3的其余部分递归调用zip谓词。

现在应该很容易实现。

但是有一个很小的问题:你仍然有太多的选择点,例如这个查询:

?- zip([1,2],X,[1,a,2,b]).

不会以简单的“是”结束,而是告诉您可能有更多解决方案(没有)。为了删除剩余选择点,您必须检查L3是否已经完全实例化(使用ground/1)并单独处理此案例。

答案 2 :(得分:0)

等长(但那些会产生一些“下沉”错误)

:- use_module(library(lambda)).
zip1(L1,L2,Z) :- scanl(\X^Y^_^[X,Y]^[X,Y],L1,L2,_,[_|Z]).
zip2(L1,L2,Z) :- maplist(\X^Y^[X,Y]^[X,Y],L1,L2,Z).

或者这个更好:

?- assertz(pair(X,Y,[X,Y])).
?- maplist(pair,[1,2,3],[4,5,6],Z).
Z = [[1, 4], [2, 5], [3, 6]].