List 将列表元素的连续副本打包到子列表中

List 将列表元素的连续副本打包到子列表中,list,prolog,dcg,List,Prolog,Dcg,我需要一些帮助。我在数据库中搜索了一下,我对这个例子很感兴趣,但是答案对我没有帮助,所以我想发布我自己的问题 任务是将列表元素的连续副本打包到子列表中: % ?- 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,

我需要一些帮助。我在数据库中搜索了一下,我对这个例子很感兴趣,但是答案对我没有帮助,所以我想发布我自己的问题

任务是将列表元素的连续副本打包到子列表中:

% ?- 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的情况)。第二个没有,我真的不知道为什么。有人能帮我解释一下这个问题吗


谢谢

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

pack([H, H|HS], [[H|TFR]|TR]):-
    pack([H|HS], [TFR|TR]).
本质上,它表示,当输入中的前两个条目相同时,第一个条目(即
H
)需要被挂起到通过递归调用
pack
规则生成的输出列表的第一个条目

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

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

前两个条款都是无关紧要的,看起来不错

第三条似乎合理。它说如果我打包
[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
| ?-

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

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

一旦我们做到了这一点,剩下的就很简单了:

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
  .                         %

要避免附加,可以使用:

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

这里建议的实现在逻辑上是纯的。它使用元谓词,它基于@false in提出的
if\u3

因此,让我们将
splitlistIfAdj/3
dif/3
结合使用,后者是
dif/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]].

我如何发布推荐信?通过发布链接。。。连续重复…:为什么
b
[a、a、a、b、c、c、a、a、d、e、e、e、e、e]
中是连续重复的…?感谢您的帮助:)b不是连续重复的,它只是显示结果应该是什么样子的一个示例。但您的问题陈述在第二个参数中只讨论了连续重复。那么为什么
b
在那里呢?嘿:谢谢你的帮助。你能向我解释一下你的意思吗?我并不“真正”理解这个程序的功能。TFR是指从静止到静止的尾巴吗?有时我在学习Prolog时会觉得很复杂。@user3637666
[[H|TFR]| TR]
是一个列表。它的第一个条目也是一个列表。“内部列表”的第一个条目是
H
,输入列表的标题(也等于输入列表的第二个条目);“内部列表”的尾部是
TFR
,它是递归调用返回的头条目。外部列表的尾部是
TR
,即从递归调用返回的输出列表的尾部。当您说Ergebnis=[[H | HH]| T]时,这是什么意思?我不知道这种语法形式。你是说头包含头和尾吗?@user3637666这意味着你有一个列表,它的头是一个列表,
[H | HH]
,它的尾是
T
。在Prolog中,您可以嵌入任意多个列表。所以
[H | T]
是一个带有头
H
和尾
T
的列表。如果
H
本身是一个列表(例如在列表列表中),那么
H
要么是空的
[]
,要么是一个由头和尾组成的列表本身。较短的版本也有一个很大的优势,即潜在的。我想知道它到底是如何编译的…@WillNess:目前使用上述代码实现的问题是索引。但除此之外,它们都使用常量空间“用于递归”。@johnjohn它可能适用于几个示例,但我要说的是,这是一个错误的实现,因为它将在其他有效情况下产生不正确的结果。仅仅为几个例子工作并不能使代码正确。我认为应该是
pack([X | Xs],[P | Ps]):-…
。如果您使用
dif/2
而不是
(\=)/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]].