【问题标题】:Decompression of a list in prologprolog中列表的解压
【发布时间】:2015-01-22 08:54:23
【问题描述】:

我需要在 prolog 中解压一个列表,如下例所示:

decode([[a,1],[b,2],[c,1],[d,3]],L). 
L = [a, b, b, c, d, d, d] ; 

我做了这个代码:

divide(L,X,Y):-length(X,1),append(X,Y,L).

divide2(L,X,Y):-divide(L,[X|_],[Y|_]).


makelist(_,N,[]):- N =< 0 .
makelist(X,Y,[X|Result]):-Y1 is Y-1,makelist(X,Y1,Result).

makelist2(L,L2):-divide2(L,X,Y),makelist(X,Y,L2).

decode([],[]).
decode([H|T],L):-makelist2(H,H2),append(H2,L,L2),decode(T,L2).

当我打电话时

makelist2([a,3],L2).
L2 = [a,a,a].

但是当我打电话时

decode([[a,3],[b,1],[c,4]],L)

连续运行。我做错了什么?

【问题讨论】:

  • 我提供了一个额外的答案,以帮助更详细地解释代码中的问题。
  • 提供了许多答案。您的问题得到解答了吗?

标签: recursion prolog predicate


【解决方案1】:

主题的另一个变体,使用了 Boris 的 repeat/3 谓词的略微修改版本:

% True when L is a list with N repeats of X
repeat([X, N], L) :-
    length(L, N),
    maplist(=(X), L).

decode(Encoded, Decoded) :-
    maplist(repeat, Encoded, Expanded),
    flatten(Expanded, Decoded).

如果Encode = [[a,1],[b,2],[c,1],[d,3]],那么在上面的decode/2中,maplist/3调用会产生Expanded = [[a],[b,b],[c],[d,d,d]],然后flatten/2调用会产生Decoded = [a,b,b,c,d,d,d]

在 SWI Prolog 中,您可以使用 append/2 而不是 flatten/2,因为您只需要在一个级别上进行“展平”。


编辑:添加一个“双向”版本,使用一点 CLPFD:
rle([], []).
rle([X], [[1,X]]).
rle([X,Y|T], [[1,X]|R]) :-
    X \== Y,     % use dif(X, Y) here, if available
    rle([Y|T], R).
rle([X,X|T], [[N,X]|R]) :-
    N #= N1 + 1,
    rle([X|T], [[N1,X]|R]).

这将产生:

| ?- rle([a,a,a,b,b], L).

L = [[3,a],[2,b]] ? ;

(1 ms) no
| ?- rle(L, [[3,a],[2,b]]).

L = [a,a,a,b,b] ? ;

no
| ?- rle([a,a,a,Y,Y,Z], [X, [N,b],[M,c]]).

M = 1
N = 2
X = [3,a]
Y = b
Z = c ? a

no
| ?- rle([A,B,C], D).

D = [[1,A],[1,B],[1,C]] ? ;

C = B
D = [[1,A],[2,B]] ? ;

B = A
D = [[2,A],[1,C]] ? ;

B = A
C = A
D = [[3,A]] ? ;

(2 ms) no
| ?- rle(A, [B,C]).

A = [D,E]
B = [1,D]
C = [1,E] ? ;

A = [D,E,E]
B = [1,D]
C = [2,E] ? ;

A = [D,E,E,E]
B = [1,D]
C = [3,E] ? ;
...

| ?- rle(A, B).

A = []
B = [] ? ;

A = [C]
B = [[1,C]] ? ;

A = [C,D]
B = [[1,C],[1,D]] ? ;
...

正如@mat 在他的评论中所建议的那样,在具有dif/2 的 Prolog 实现中,dif(X,Y) 比上面的X \== Y 更可取。

【讨论】:

  • 一个非常好的解决方案。我只是对flatten 有一种病态的厌恶:) 但在这种情况下,它允许一个干净的解决方案。
  • 引自 the docs,“最终需要 flatten/3 通常表明......一个糟糕的设计。”,但您实际上并不需要 flatten/2,只有 flatten_one_level/2,更为人所知作为 FP 世界中的concat。这是非常好的和花花公子。
  • @WillNess 对。我习惯于通过 GNU Prolog 做事。在 SWI 中,我可以使用 append/2
  • +1 用于尝试全方位解决方案。在 SICStus Prolog、SWI 和其他几个中,您可以使用 dif/2 而不是 (\==)/2 来使其正确。
【解决方案2】:

问题出在decode最后一个子句中appenddecode的顺序。尝试追踪它,或者更好的是,“手动”追踪它,看看会发生什么。

另一种方法:参见this answer。因此,repeat/3 定义为:

% True when L is a list with N repeats of X
repeat(X, N, L) :-
    length(L, N),
    maplist(=(X), L).

您可以将decode/2 写为:

decode([], []).
decode([[X,N]|XNs], Decoded) :-
    decode(XNs, Decoded_rest),
    repeat(X, N, L),
    append(L, Decoded_rest, Decoded).

但这是一种稍微迂回的方法。您可以定义repeat/3 的差异列表版本,称为repeat/4

repeat(X, N, Reps, Reps_back) :-
    (   succ(N0, N)
    ->  Reps = [X|Reps0],
        repeat(X, N0, Reps0, Reps_back)
    ;   Reps = Reps_back
    ).

然后您可以使用decode/2decode_1/3 的差异列表版本

decode(Encoded, Decoded) :-
    decode_1(Encoded, Decoded, []).

decode_1([], Decoded, Decoded).
decode_1([[X,N]|XNs], Decoded, Decoded_back) :-
    repeat(X, N, Decoded, Decoded_rest),
    decode_1(XNs, Decoded_rest, Decoded_back).


?- decode([[a,1],[b,2],[c,1],[d,3]],L).
L = [a, b, b, c, d, d, d].

?- decode([[a,3],[b,1],[c,0],[d,3]],L).
L = [a, a, a, b, d, d, d].

?- decode([[a,3]],L).
L = [a, a, a].

?- decode([],L).
L = [].

【讨论】:

  • 非常感谢,我测试过,它可以工作,但我在 prolog 中很新,这段代码超出了我理解语言的能力。我可以在我的示例中理解我做错了什么吗?
  • @MoroianuAlexandru 请看我回答的第一段
【解决方案3】:

您可以使用此代码处理两个方向:

:- use_module(library(lambda)).
% code from Pascal Bourguignon
packRuns([],[]).

packRuns([X],[[X]]).

packRuns([X|Rest],[XRun|Packed]):-
    run(X,Rest,XRun,RRest),
    packRuns(RRest,Packed).


run(Var,[],[Var],[]).

run(Var,[Var|LRest],[Var|VRest],RRest):-
    run(Var,LRest,VRest,RRest).

run(Var,[Other|RRest],[Var],[Other|RRest]):-
    dif(Var,Other).
%end code

pack_1(In, Out) :-
    maplist(\X^Y^(X = [V|_],
              Y = [V, N],
              length(X, N),
              maplist(=(V), X)),
        In, Out).

decode(In, Out) :-
    when((ground(In); ground(Out1)),pack_1(Out1, In)),
    packRuns(Out, Out1).

输出:

 ?- decode([[a,1],[b,2],[c,1],[d,3]],L).

L = [a, b, b, c, d, d, d] .

 ?- decode(L, [a,b,b,c,d,d,d]).

L = [[a, 1], [b, 2], [c, 1], [d, 3]] .

【讨论】:

  • 很好地介绍了关系解决方案 (+1)。虽然,decode([[a,1],[c,0],[b,3]], L). 失败了。但对于关系解决方案,也许期望计数为&gt; 0 是有道理的。
【解决方案4】:

一种紧凑的方式:

decode(L,D) :- foldl(expand,L,[],D).
expand([S,N],L,E) :- findall(S,between(1,N,_),T), append(L,T,E).

findall/3 这是“老式”的 Prolog 列表理解工具

【讨论】:

  • 一如既往的好答案。我不确定,但是您可以使用四个参数findall/4 来完全避免expand/3 中的append 吗?
  • @Boris:实际上,我试过了,但它给出了相反的答案:expand([S,N],L,E) :- findall(S,between(1,N,_),E,L).
【解决方案5】:

decode 对你的谓词来说是个糟糕的名字:做得好,你的谓词应该是双向的——如果你说

decode( [[a,1],[b,2],[c,3]] , L )

你应该得到

L = [a,b,b,c,c,c].

如果你说

decode( L , [a,b,b,c,c,c] ) .

你应该得到

L = [[a,1],[b,2],[c,3]].

所以我会使用不同的名称,例如run_length_encoding/2。我也可能不会使用列表来表示单个运行长度,因为[a,1] 是这个序言词:.(a,.(1,[])。只需对 arity 2 使用一个简单的术语——我自己,我喜欢使用 :/2,因为它被定义为中缀运算符,所以你可以简单地说 a:1

试穿一下尺寸:

run_length_encoding( []     , []     ) .  % the run-length encoding of the empty list is the empty list.
run_length_encoding( [X|Xs] , [R|Rs] ) :- % the run-length encoding of a non-empty list is computed by
  rle( Xs , X:1 , T , R ) ,               % - run-length encoding the prefix of the list
  run_length_encoding( T , Rs )           % - and recursively run-length encoding the remainder
  .                                       % Easy!

rle( []     , C:N , []     , C:N ) .  % - the run is complete when the list is exhausted.
rle( [X|Xs] , C:N , [X|Xs] , C:N ) :- % - the run is complete,
  X \= C                              % - when we encounter a break
  .                                   %
rle( [X|Xs] , X:N , T      , R   ) :- % - the run continues if we haven't seen a break, so....
  N1 is N+1 ,                         % - increment the run length,
  rle( Xs, X:N1, T, R )               % - and recurse down.
  .                                   % Easy!

【讨论】:

  • run_length_encoding([a,b,b,c,c,c], L). 产生L = [a:1,b:2,c:3],但run_length_encoding(L, [a:1,b:2,c:3] ). 导致堆栈溢出。
【解决方案6】:

直接回答最初的问题,我做错了什么?...

当我运行原始代码时,任何预期的用例“无限期地运行”而没有产生结果。

通读主谓词:

decode([],[]).

这表示[] 是解码[] 的结果。听起来不错。

decode([H|T],L) :- makelist2(H,H2), append(H2,L,L2), decode(T,L2).

这表示L 是解码[H|T] 的结果,如果H2H 的扩展(这是makelist2 所做的......也许 - 我们将在下面讨论),和H2 附加到这个结果给出了另一个列表L2,它是原始尾部T 的解码形式。这听起来不正确。如果我解码[H|T],我应该(1)扩展H,(2)解码TL2,然后(3)将H附加到L2L

所以修正后的第二个子句是:

decode([H|T], L) :- makelist2(H, H2), decode(T, L2), append(H2, L2, L).

注意append/3 的参数顺序,并且调用发生在尾部的decode 之后。正如鲍里斯之前指出的那样,append 和递归decode 的错误顺序会导致连续运行而没有任何输出,因为append 带有更多未实例化的参数会在decode 成功之前产生大量不需要的可能性。

但现在的结果是:

| ?- decode([[a,3]], L).

L = [a,a,a] ? ;

L = [a,a,a,a] ? ;
...

如果您在 Prolog 解释器中手动尝试我们的其他谓词,您会发现 makelist2/2 有问题:

它会产生正确的结果,但也会产生一堆不正确的结果。让我们看看makelist2/2。我们可以自己试试这个谓词,看看会发生什么:

| ?- makelist2([a,3], L).

L = [a,a,a] ? ;

L = [a,a,a,a] ? ;
...

有一个问题:makelist2/2 应该只给出第一个解决方案,但它继续运行,给出不正确的解决方案。让我们仔细看看makelist/2

makelist2(L,L2) :- divide2(L,X,Y), makelist(X,Y,L2).

它采用[A,N] 形式的列表L,将其(通过divide2/3)划分为X = AY = N,然后调用辅助makelist(X, Y, L2)

makelist(_,N,[]):- N =< 0 .
makelist(X,Y,[X|Result]):-Y1 is Y-1,makelist(X,Y1,Result).

makelist/3 应该通过将第一个参数复制第二个参数中给出的次数来生成一个列表(第三个参数)。第二个,递归子句似乎没问题,但有一个重要缺陷:即使Y 的值小于或等于0,它也会成功。因此,即使找到了正确的解决方案,它也会在不正确的解决方案上继续成功,因为基本情况允许计数为 =&lt; 0

| ?- makelist(a,2,L).

L = [a,a] ? ;

L = [a,a,a] ? ;

我们可以修复makelist/2如下:

makelist(_,N,[]):- N =< 0 .
makelist(X,Y,[X|Result]):- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).

现在代码将生成正确的结果。我们只需要修复decode/2的第二个子句和makelist/3的第二个子句。

| ?- decode([[a,3],[b,4]], L).

L = [a,a,a,b,b,b,b]

yes

仅包含这几处更正的完整原始代码如下所示:

divide(L, X, Y) :- length(X, 1), append(X, Y, L).

divide2(L, X, Y) :- divide(L, [X|_], [Y|_]).

makelist(_, N, []) :- N =< 0 .
makelist(X, Y, [X|Result]) :- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).

makelist2(L, L2) :- divide2(L, X, Y), makelist(X, Y, L2).

decode([], []).
decode([H|T], L) :- makelist2(H,H2), decode(T,L2), append(H2,L2,L).

注意一些简单直接的改进。谓词 divide2(L, X, Y) 接受两个元素的列表 L 并产生每个单独的元素 XY。这个谓词是不必要的,因为在 Prolog 中,您可以通过简单的统一获得这些元素:L = [X, Y]。你可以在 Prolog 解释器中试试这个:

| ?- L = [a,3], L = [X,Y].

L = [a,3]
X = a
Y = 3

yes

然后我们可以完全删除divide/3divide2/3 谓词,并将对divide2(L, X, Y) 的调用替换为L = [X,Y] 并将makelist2/2 减少为:

makelist2(L, L2) :- L = [X, Y], makelist(X, Y, L2).

或者更简单(因为我们可以在子句的头部进行统一):

makelist2([X,Y], L2) :- makelist(X, Y, L2).

您可以删除makelist2/2 并直接从decode/2 调用makelist/2,方法是将H 直接与其两个元素[X, N] 统一起来。所以原代码简化为:

makelist(_, N, []) :- N =< 0 .
makelist(X, Y, [X|Result]) :- Y > 0, Y1 is Y-1, makelist(X,Y1,Result).

decode([], []).
decode([[X,N]|T], L) :- makelist(X, N, H2), decode(T, L2), append(H2, L2, L).

并且makelist/3 可以使用其他答案中提供的一种方法更清楚地执行(例如,请参阅 Boris 的 repeat/3 谓词)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-24
    • 2013-12-08
    • 2017-09-29
    • 1970-01-01
    • 2021-12-22
    • 2016-12-26
    相关资源
    最近更新 更多