【问题标题】:Split a list in two halves, reversing the first half using difference lists将列表分成两半,使用差异列表反转前半部分
【发布时间】:2019-08-18 19:16:20
【问题描述】:

我需要做一个类似这样的练习: Prolog - Split a list in two halves, reversing the first half.

我被要求将一个字母列表放入两个列表中虽然我在做,但只使用差异列表。

这些是必需的查询和输出

?-dividelist2([a,b,c,d,e,f | T] - T, L1-[], L2-[]).
L1 = [c,b,a]
L2 = [d,e,f]

?-dividelist2([a,b,c,d,e | T] - T, L1-[], L2-[]).
L1 = [c,b,a]
L2 = [d,e]
% OR
L1 = [b,a]
L2 = [c,d,e]

这是我使用上一个示例的代码,但经过修改,我不知道如何正确比较两个列表 从输入中“扣除”它们并产生[d,e,f]?

dividelist2(In -[], L1-[], L2-[]) :-
     length_dl(In - [],L), % length of the list
     FL is L//2, % integer division, so half the length, Out1 will be 1 shorter than Out2 if L is odd
    ( \+ (FL*2 =:= L), % is odd
      FLP is FL + 1 % odd case
    ; FLP = FL % odd and even case
    ), 
    take(In,FLP,FirstHalf),
    conc([FirstHalf| L2]-l2,L2-[],In-[]),
    reverse1(FirstHalf-[], L1-[]). % do the reverse

reverse1(A- Z,L - L):-
  A == Z , !. 
reverse1([X|Xs] - Z,L - T):-
  reverse1(Xs - Z, L - [X|T]).

length_dl(L- L,0):-!.
length_dl([X|T] - L,N):-
    length_dl(T- L,N1),
    N is N1 + 1 .
take(Src,N,L) :- findall(E, (nth1(I,Src,E), I =< N), L).
conc(L1-T1,T1-T2,L1-T2).

这是当前的轨迹:

Call:dividelist2([a, b, c, d, e, f|_22100]-_22100, _22116-[], _22112-[])
 Call:length_dl([a, b, c, d, e, f]-[], _22514)
 Call:length_dl([b, c, d, e, f]-[], _22520)
 Call:length_dl([c, d, e, f]-[], _22526)
 Call:length_dl([d, e, f]-[], _22532)
 Call:length_dl([e, f]-[], _22538)
 Call:length_dl([f]-[], _22544)
 Call:length_dl([]-[], _22550)
 Exit:length_dl([]-[], 0)
 Call:_22554 is 0+1
 Exit:1 is 0+1
 Exit:length_dl([f]-[], 1)
 Call:_22560 is 1+1
 Exit:2 is 1+1
 Exit:length_dl([e, f]-[], 2)
 Call:_22566 is 2+1
 Exit:3 is 2+1
 Exit:length_dl([d, e, f]-[], 3)
 Call:_22572 is 3+1
 Exit:4 is 3+1
 Exit:length_dl([c, d, e, f]-[], 4)
 Call:_22578 is 4+1
 Exit:5 is 4+1
 Exit:length_dl([b, c, d, e, f]-[], 5)
 Call:_22584 is 5+1
 Exit:6 is 5+1
 Exit:length_dl([a, b, c, d, e, f]-[], 6)
 Call:_22590 is 6//2
 Exit:3 is 6//2
 Call:3*2=:=6
 Exit:3*2=:=6
 Call:_22590=3
 Exit:3=3
 Call:take([a, b, c, d, e, f], 3, _22594)
 Call:'$bags' : findall(_22518, (nth1(_22514, [a, b, c, d, e, f], _22518),_22514=<3), _22614)
 Exit:'$bags' : findall(_22518, '251db9a2-f596-4daa-adae-38a38a13842c' : (nth1(_22514, [a, b, c, d, e, f], _22518),_22514=<3), [a, b, c])
 Exit:take([a, b, c, d, e, f], 3, [a, b, c])
 Call:conc([[a, b, c]|_22112]-l2, _22112-[], [a, b, c, d, e, f]-[])
 Fail:conc([[a, b, c]|_22112]-l2, _22112-[], [a, b, c, d, e, f]-[])
 Fail:dividelist2([a, b, c, d, e, f|_22100]-_22100, _22116-[], _22112-[])
false

谢谢

【问题讨论】:

  • 我今天不能给出完整的答案,但是输入差异列表可以让您轻松访问列表的开头和结尾。您可以使用它来递归地从正面和背面取出元素,并使用它们来构建 2 个常规列表(或差异列表,但不需要孔)。 Prolog队列实现的工艺有语法,你可以追踪找到2个基本情况。

标签: prolog difference-lists


【解决方案1】:

这不是答案,而是不符合评论长度限制的测试和调试建议。建议使用 Logtalk,您可以在大多数 Prolog 系统上运行。

根据您的问题,dividelist2/3 谓词需要满足几个属性,其中一个描述结果列表的长度。我们可以使用谓词p/1 轻松表达此属性:

p(DL) :-
    difflist::length(DL, N),
    dividelist2(DL, DL1, DL2),
    difflist::length(DL1, N1),
    difflist::length(DL2, N2),
    N is N1 + N2,
    abs(N1 - N2) =< 1.

这里我使用 Logtalk 的 difflist 库对象来计算差异列表的长度。有了这个谓词,我们现在可以对您的dividelist2/3 谓词执行一些属性测试

使用Logtalklgtunit工具实现属性测试,我们得到:

?- lgtunit::quick_check(p(+difference_list(integer))).
*     quick check test failure (at test 1 after 0 shrinks):
*       p(A-A)
false.

即对于空差异列表的琐碎情况,您的代码会失败。在查询中,我们使用difference_list(integer) 类型只是为了简化生成的反例。

让我们尝试通过在您的代码中添加以下子句来修复失败:

dividelist2(A-A, B-B, C-C).

重新尝试我们的测试查询,我们现在得到:

?- lgtunit::quick_check(p(+difference_list(integer))).
*     quick check test failure (at test 2 after 0 shrinks):
*       p([0|A]-A)
false.

dividelist2/3 谓词对于具有单个元素的差异列表失败。您现在可以使用生成的反例中的差异列表作为调试的起点:

?- dividelist2([0|A]-A, L1, L2).
A = [0|A],
L1 = _2540-_2540,
L2 = _2546-_2546 ;
false.

您还可以对辅助谓词使用属性测试。采用length_dl/2 谓词。我们可以将它与计算差异列表长度的谓词的另一个实现进行比较,例如Logtalk 库中的一个,通过定义另一个属性:

q(DL) :-
    difflist::length(DL, N),
    length_dl(DL, N).

测试我们得到:

?- lgtunit::quick_check(q(+difference_list(integer))).
*     quick check test failure (at test 3 after 0 shrinks):
*       q([-113,446,892|A]-A)
false.

有效地,使用 counter.example,我们得到:

?- length_dl([-113,446,892|A]-A, N).
A = [-113, 446, 892|A],
N = 0.

希望这种见解有助于修复您的代码。

【讨论】:

    【解决方案2】:

    好的,我的想法可行,但似乎有些不雅。我们将从一个方便的实用程序开始,它将列表转换为差异列表:

    list_dl([], W-W).
    list_dl([H|T1], [H|T2]-W) :-
        list_dl(T1, T2-W).
    

    现在我们想要一个谓词从差异列表中获取第一个和最后一个元素。只剩下一个元素的情况需要以不同方式处理,因此我们将使其唯一。

    head_last(Head, Head, DL-Hole, one) :-
        once(append([Head|_], [Last, Hole], DL)),
        var(Last), !.
    head_last(Head, Last, DL-Hole, New) :-
        once(append([Head|Mid], [Last, Hole], DL)),
        list_dl(Mid, New).
    

    现在我们可以创建递归拆分和反向谓词,它有 3 个基本情况:

    splitrev(W-W, [], []) :- var(W), !. % Empty base case.
    splitrev(DL, [V|[]], []) :- head_last(V, V, DL, one).
    splitrev(DL, [], [V|[]]) :- head_last(V, V, DL, one).
    splitrev(DL, [Head|Front], [Last|Back]) :-
        head_last(Head, Last, DL, Rest),
        splitrev(Rest, Front, Back).
    

    不幸的是,将一个元素添加到差异列表的后面比从后面获取一个元素要容易得多,而且让该元素填补了列表中的漏洞。因此,我认为不同的策略会更好。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-09-21
      • 2010-09-07
      • 2010-10-19
      • 1970-01-01
      • 2017-02-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多