【问题标题】:Pack consecutive duplicates of list elements into sublists将列表元素的连续重复项打包到子列表中
【发布时间】:2015-06-05 19:16:36
【问题描述】:

我需要一些帮助。我搜索了数据库并found one question already asked 关于这个例子,但答案并没有真正帮助我,所以我想发布我自己的问题。

任务是将列表元素的连续重复打包成子列表:

% ?- pack([a,a,a,a,b,c,c,a,a,d,e,e,e,e],X).
% X = [[a,a,a,a],[b],[c,c],[a,a],[d],[e,e,e,e]].

这是我得到的:

pack([], []).
pack([X], [[X]]).
pack(Liste, Ergebnis):-
   Liste = [H, T|TS],
   H \= T,
   pack([T|TS], Ergebnis1),
   append([[H]], Ergebnis1, Ergebnis).
pack([H, H|HS], Ergebnis):-
   pack([H|HS], Ergebnis1),
   append([H|HS], Ergebnis1, Ergebnis).

第一种情况非常有效(H \= T 的情况)。第二个没有,我真的不知道为什么。有人可以帮助我并根据我的解决方案解释问题吗?

谢谢

【问题讨论】:

  • 如何发布参考?通过发布链接
  • ...连续重复...:为什么b[a,a,a,a,b,c,c,a,a,d,e,e,e,e]中是连续重复的?
  • b 不是连续重复,它只是显示结果应该是什么样子的示例。
  • 但是您的问题陈述仅涉及第二个参数中的连续重复。那么为什么b 在那里呢?

标签: list prolog


【解决方案1】:

您的第四个子句试图将[H|HS] 附加到结果中,这是不正确的,因为[H|HS] 是原始列表的尾部。你可以让它变得更简单 -

pack([H, H|HS], [[H|TFR]|TR]):-
    pack([H|HS], [TFR|TR]).

本质上,它表示当输入中的前两个条目相同时,第一个条目(即H)需要预先添加到由递归调用产生的输出列表的第一个条目@ 987654326@规则。

请注意,第三个子句也可以通过将您立即“破解”的Liste 参数替换为子句标题中“内联”的“破解”版本来简化,并对输出执行相同操作变量Ergebnis1。最终版本应如下所示:

pack([H, T|TS], [[H]|TR]):-
    H \= T,
    pack([T|TS], TR).

Here is a demo on ideone.

【讨论】:

  • 嘿 :D 谢谢你的帮助。你能解释一下这个 [[H|TFR]|TR] 是什么意思吗?我并不“真正”了解该程序的作用。 TFR 是指休息的尾巴吗?有时我在尝试学习 Prolog 时会觉得很复杂。
  • @user3637666 [[H|TFR]|TR] 是一个列表。它的第一个条目也是一个列表。该“内部列表”的第一个条目是H,输入列表的头部(也等于输入列表的第二个条目); “内部列表”的尾部是TFR,它是递归调用返回的头部条目。 outer 列表的尾部是TR,即递归调用返回的输出列表的尾部。
【解决方案2】:

前两个子句是琐碎的,看起来不错。

第三个条款似乎是合理的。它说如果我打包[H,T|TS]H \= T,那么结果将是[H] 附加在[T|TS] 被打包的结果之前。

第四条是问题:

pack([H, H|HS], Ergebnis):-
    pack([H|HS], Ergebnis1),
    append([H|HS], Ergebnis1, Ergebnis).

它表示打包[H,H|HS] 的结果与打包[H|HS] 然后在前面添加所有[H|HS] 的结果相同。这似乎不合逻辑。当你打包[H|HS] 时会发生什么,你会得到类似[[H,...,H]|X] 的东西,现在你想把另一个H 放到第一个子列表的头部。所以你应该有:

pack([H, H|HS], Ergebnis):-
    pack([H|HS], [HH|T]),
    Ergebnis = [[H|HH]|T].

您可以使这些谓词更紧凑:

pack([], []).
pack([X], [[X]]).
pack([H,T|TS], [[H]|PTS]):-
   H \= T,
   pack([T|TS], PTS).
pack([H, H|HS], [[H|HH]|T]):-
   pack([H|HS], [HH|T]).

测试...

| ?- pack([a,a,a,a,b,c,c,a,a,d,e,e,e,e],X).

X = [[a,a,a,a],[b],[c,c],[a,a],[d],[e,e,e,e]] ? a

no
| ?-

【讨论】:

  • 当您说 Ergebnis = [[H|HH]|T] 时,这是什么意思?我不知道这种形式的语法。你是说Head包含一个head和一个Tail?
  • @user3637666 这意味着你有一个列表,它的头部是一个列表[H|HH],它的尾部是T。在 Prolog 中,您可以嵌入任意数量的列表。所以[H|T] 是一个带有头部H 和尾部T 的列表。如果H 本身是一个列表(例如在一个列表列表中),那么H 要么是空的[],要么是一个由头部和尾部组成的列表。
  • 更短的版本还有一个很大的优势是TRMC - 可能。我想知道它是如何真正编译的......
  • @WillNess:上述代码的当前实现中的问题是索引。但除此之外,它们都使用常量空间“进行递归”。
  • @johnjohn 它可能适用于几个示例,但我说这是一个错误的实现,因为它会为其他有效案例产生不正确的结果。仅仅为几个例子工作并不能使代码正确。
【解决方案3】:

每个在这个问题的答案中给出的其他实现都是逻辑上不纯的。它们不是单调的,因此在与非基本术语一起使用时很容易在逻辑上变得不合理。

这里建议的实现是逻辑上纯粹的。它使用元谓词splitlistIfAdj/3,它基于if_/3,由@false 在an answer on reification 中提出。

所以让我们将splitlistIfAdj/3dif/3 结合使用,dif/3dif/2 的具体变体:

?- splitlistIfAdj(dif,[a,a,a,a,b,c,c,a,a,d,e,e,e,e],Lists).
Lists = [[a,a,a,a],[b],[c,c],[a,a],[d],[e,e,e,e]]. % succeeds deterministically

当我们留在单调这里时, 即使我们使用非基本术语,我们也会得到有意义的答案:

?- splitlistIfAdj(dif,[A,B,C],Lists).
A = B,    B = C,    Lists = [[C, C, C]]    ;
A = B,    dif(B,C), Lists = [[B, B], [C]]  ;
dif(A,C), B = C,    Lists = [[A], [C, C]]  ;
dif(A,B), dif(B,C), Lists = [[A], [B], [C]].

【讨论】:

    【解决方案4】:

    我会以不同的方式处理问题,并将问题分解为两个更简单的任务。

    我们首先需要能够将列表划分为一个前缀,该前缀由列表头部的连续元素列表及其后缀(所有其他)组成:

    partition( []      , []    , []     ) .
    partition( [X]     , [X]   , []     ) .
    partition( [X,Y|Z] , [X]   , [Y|Z]  ) :- X \= Y .
    partition( [X,Y|Z] , [X|P] , S      ) :- X  = Y , partition([Y|Z],P,S) .
    

    有了这些,剩下的就很简单了:

    pack([],[]) .               % packing an empty list yields the empty list
    pack([X|Xs],[P,Ps]) :-      % packing a non-empty list consists of
      partition([X|Xs],P,T) ,   % - partitioning it into its prefix and suffix, and
      pack(T,Ps)                % - packing the suffix
      .                         %
    

    【讨论】:

    • 我觉得应该是pack([X|Xs],[P|Ps]) :- ...
    【解决方案5】:

    为避免追加,您可以使用:

    pack(L, LP) :-                  % pack/2
        phrase(pack(L), LP).
    
    pack([]) --> [].                % pack//1
    pack([X]) --> [[X]].
    pack([H, H1 | T]) -->
         {H \= H1},
         [[H]],
         pack([H1 | T]).
    pack([H, H | T]) -->
        pack([H | T], [H]).
    
    pack([H, H | T], P) -->         % pack//2
        pack([H | T], [H|P]).
    pack([H, H1 | T], P) -->
        {H \= H1},
        [[H | P]],
        pack([H1 | T]).
    pack([H, H], P) -->
        [[H,H|P]].
    

    【讨论】:

    • 如果您使用dif/2 而不是(\=)/2,您的代码将继续使用logical-purity
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-01-25
    • 2019-08-01
    • 1970-01-01
    • 2019-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多