我正在尝试创建一个函数,将可变长度列表按顺序拆分为三个偶数长度列表。以下将其拆分为三个,但进程一次一个地将它们插入到每个列表中。
我想要的一个例子是:
[1, 2, 3, 4, 5] -> [1, 2], [3, 4], [5]
另一个例子是:
[8, 7, 6, 5, 4, 3, 2, 1] -> [8, 7, 6], [5, 4, 3], [2, 1].
以下代码通过一次插入一个列表来分割它们:
div([], [], [], []).
div([X], [X], [], []).
div([X,Y], [X], [Y], []).
div([X,Y,Z|End], [X|XEnd], [Y|YEnd], [Z|ZEnd]):-
div(End, XEnd, YEnd, ZEnd).
此代码输出:
[1, 2, 3, 4, 5] -> [1, 4], [2, 5], [3]
如何解决此问题?
答案 0 :(得分:5)
@Boris的答案在第一个参数列表的长度未知时不会终止。要看到这一点,我们无需再使用failure-slice来查看第一个目标:
div(L, L1, L2, L3) :- length(L, Len), false,% here you compute for example Len1 and Len2length(L1, Len1),length(L2, Len2),append(L1, L1_suffix, L),append(L2, L3, L1_suffix).
另一方面,您的原始程序有quite nice termination properties。 cTI给出了以下最佳终止特性:
div(A,B,C,D) terminates_if b(A);b(B);b(C);b(D).
换句话说,为确保终止,您只需要一个参数(A
或B
或C
或D
)作为具体列表有限的和地面的(这就是b(..)
的含义)。这是一个非常强大的终止条件。这些论点不合适真的很可惜!为什么不概括你的程序?唯一的问题就是它限制了列表元素。所以我将用_
s替换列表元素的所有变量名:
gdiv([], [], [], []).
gdiv([_], [_], [], []).
gdiv([_,_], [_], [_], []).
gdiv([_,_,_|End], [_|XEnd], [_|YEnd], [_|ZEnd]):-
gdiv(End, XEnd, YEnd, ZEnd).
very same终止属性适用于此程序。
唉,现在有点太笼统了。鲍里斯的解决方案现在可以改变用途:divnew(Zs, As, Bs, Cs) :-
gdiv(Zs, As, Bs, Cs),
append(As, BsCs, Zs),
append(Bs, Cs, BsCs).
我表达同样的首选方式是:
divnew(Zs, As, Bs, Cs) :-
gdiv(Zs, As, Bs, Cs),
phrase( ( seq(As), seq(Bs), seq(Cs) ), Zs).
有关seq//1
的定义,请参阅other answers。
答案 1 :(得分:2)
div(L, L1, L2, L3) :-
append(L1, L1_suffix, L),
append(L2, L3, L1_suffix).
你看到这如何拆分三个名单?现在,您不能说明您希望列表L1
,L2
和L3
有多长。您可以使用length/2
获取L
的长度,并设置三个结果的长度,如果您不希望谓词像目前那样一般。
因为你说"相对均匀的长度",这是相对的,我需要以某种方式解释它,让我们假设你的意思是,对于正整数len和n,len = 3n,你得到len1 = len2 = len3 = n,对于k = 3n + 1,你得到len1 = n + 1,len2 = len3 = n,对于k = 3n + 2,你得到len1 = len2 = n + 1,len3 = n。我让你弄清楚如何计算长度。
div(L, L1, L2, L3) :-
length(L, Len),
% here you compute for example Len1 and Len2
length(L1, Len1),
length(L2, Len2),
append(L1, L1_suffix, L),
append(L2, L3, L1_suffix).