我是Prolog的新手,我的任务要求我们实现如下所述的功能:
编写一个Prolog谓词zip(L1,L2,L3)
如果列表L3
是通过压缩(即混洗“或交错”)列表L1
和{{1 }}。更新:列表L2
和L1
可以有不同的长度。例如,完成后,您应该得到以下行为:
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中的语法和循环。
答案 0 :(得分:7)
实际上,我有三个答案。你可能想要第三个。但无论如何都要经历其他人。
我不确定你想要你所描述的关系。你是 同时学习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 maplist
。 maplist
要求所有列表的长度相同。
(编辑)另一种解释如下。名称为interlace/3
或shuffle/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].
我们以这种方式问:Ys
和Zs
如何看起来像shuffle / 3是真的?事实上,Ys
有3个答案:
[]
是空列表。然后Zs
为[1,2]
。所以这是一个解决方案。
[_G607]
是一个只包含一个元素的列表。 Zs
是[1,_G607,2]
。 _G607
是一个自由变量。它可能有一个更好的名称,但关键是这个变量在Ys
和Zs
内发生 。这个答案说:适合该变量的所有术语都是解决方案。所以我们在这里有无限多个解决方案,只需一个答案。
[_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
中是否存在同一变量的重复项!
也许这就是你真正想要的东西:
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]].