【问题标题】:Prolog compress list with quantity -- repeated answersProlog 压缩列表与数量 - 重复的答案
【发布时间】:2019-02-11 04:06:15
【问题描述】:

我有一个元素列表,其中包含一个人的朋友数量。

[friends(mike, 4), friends(joe, 3), friends(mike, 1), friends(mike, 2)]

我想压缩这个列表并获得以下内容

[friends(mike, 7), friend(joe, 3)]

我创建了成员,并删除了第一个外观。

member(E, [E|_]).
member(E, [_|Y]) :- 
    member(E, Y).

delete_first([], _, []).
delete_first([X|Y], X, Y).
delete_first([X|Y], E, [X|L]) :- 
    X \= E, 
    delete_first(Y, E, L).

compress([], []).
compress([friends(P, C)|R], S) :- 
    member(friends(P, X), R), 
    delete_first(R, friends(P, X), E), 
    N is C + X, 
    compress([friends(P, N)|E], S).
compress([friends(P, C)|R], [friends(P, C)|S]) :- 
    not(member(friends(P, _), R)), 
    compress(R, S).

我的答案是正确的,但 Prolog 多次返回相同的答案。为什么会这样?

例子:

?- compress([friends(mike, 4), friends(joe, 3), friends(mike, 1), 
             friends(mike, 2), friends(joe,4), friends(mike, 3)],X).
X = [friends(mike, 10), friends(joe, 7)] ;
X = [friends(mike, 10), friends(joe, 7)] ;
X = [friends(mike, 10), friends(joe, 7)] ;
X = [friends(mike, 10), friends(joe, 7)] ;
X = [friends(mike, 10), friends(joe, 7)] ;
X = [friends(mike, 10), friends(joe, 7)] ;
false.

【问题讨论】:

  • @WillNess 添加了查询和结果。
  • 承诺:将尽快为不需要实例化错误以防止出现错误情况的纯版本提供赏金。

标签: list prolog compression


【解决方案1】:

另一种方法是使用 aggregate/3(与 SWI-Prolog 一起使用):

compress(In, Out) :-
     aggregate(set(friends(P,S)), aggregate(sum(X), member(friends(P,X), In), S), Out).

结果:

?- compress([friends(mike, 4), friends(joe, 3), friends(mike, 1),friends(mike, 2), friends(joe,4), friends(mike, 3)],X).
X = [friends(joe, 7), friends(mike, 10)].

【讨论】:

  • +1:library(aggregate) 经常没有代表......应该是首选武器,因为人们对聚合 SQL 运算符有足够的信心
【解决方案2】:

一个小改动解决了重复答案的问题:

....
....
compress([], []).
compress([friends(P, C)|R], S) :- 
    % member(friends(P, X), R), !,          NB   either add a cut here
    % \+( \+( member(friends(P, X), R))),   NB   or use double negation
    memberchk(friends(P, X), R),            NB   or use `memberchk/2` if available
    delete_first(R, friends(P, X), E), 
....
....

这也提供了解释:member 在列表中有重复项时多次成功,但您只打算使用第一个结果。

【讨论】:

  • 我怀疑membermemberchk 后跟delete_firstselect 后跟一个cut 是一样的。
  • 还有selectchk/3,我想。一定会做类似的事情。 :) 有趣的是,双重否定也有效(至少对于给定的查询......我的意思是,它留下了 X 未实例化......)。
  • 是的,确实有:)
  • 我猜你的意思是\+ (member(X, [1,2,3]), writeln(X), false).而不是(member(X, [1,2,3]), writeln(X), false ; true).
  • 差不多。我的意思是\+ ( Generator, \+ Goal ),例如\+ ( member(X, [1,2,3]), \+ writeln(X) )
【解决方案3】:

很抱歉没有真正回答您的问题,而是为您提供了替代解决方案。

你做的太绕了,没有任何明显的好处(但如果我错了,请纠正我)。

一种惯用的方法是使用msort/2 在不删除重复项的情况下进行排序。这将使您需要聚合的条目彼此相邻。这样算起来就容易了。

如果你也使用group_pairs_by_key/2,那就更简单了:

friends_compressed(L, C) :-
    maplist(friends_pair, L, Pairs),
    msort(Pairs, Sorted),
    group_pairs_by_key(Sorted, Grouped),
    maplist(counts_sum, Grouped, Summed),
    maplist(friends_pair, C, Summed).

friends_pair(friends(Name, Number), Name-Number).

counts_sum(X-Counts, X-Sum) :-
    sum_list(Counts, Sum).

大部分代码都是从friends(Name, Count) 转换为Name-Count,但这不是重点。

最终结果的唯一区别是列表的顺序是按名称而不是按在原始列表中的第一次出现:

?- friends_compressed([friends(mike, 4), friends(joe, 3), friends(mike, 1), friends(mike, 2)], R).
R = [friends(joe, 3), friends(mike, 7)].

group_pairs_by_key/2sum_list/2的定义可以在SWI-Prolog的源码中查找。

【讨论】:

    【解决方案4】:

    如果你改变delete_first/3的定义...

    delete_first([X|Y], X, Y).
    delete_first([X|Y], E, [X|L]) :- 
       X \= E, 
       delete_first(Y, E, L).
    

    ...您不再需要使用member/2...

    compress([], []).
    compress([friends(P,C)|R], S) :- 
       delete_first(R, friends(P,X), E), 
       N is C + X, 
       compress([friends(P,N)|E], S).
    compress([friends(P,C)|R], [friends(P,C)|S]) :- 
       \+ delete_first(R, friends(P,_), _),
       compress(R, S).
    

    ...您的示例查询中的重复答案消失了:

    ?- compress([friends(mike,4), friends(joe,3),
                 friends(mike,1), friends(mike,2),
                 friends(joe,4),  friends(mike,3)], Xs).
    Xs = [friends(mike, 10), friends(joe, 7)] ;
    false.
    

    但是,如果在没有充分实例化的情况下使用,compress/2 可能会给出虚假答案:

    ?- 压缩([friends(mike,4), friends(joe,3), friends(Any,10)], Xs)。 Any = mike, Xs = [friends(mike,14),friends(joe,3)] ; 错误的。 % 什么?!任何=乔怎么样?

    为了防止这种情况,我们可以像这样使用iwhen/2

    list_compressed(Es, Xs) :-
       iwhen(ground(Es), compress(Es,Xs)).
    

    示例查询:

    ?- list_compressed([friends(mike,4), friends(joe,3), friends(Any,10)], Xs)。 错误:参数没有充分实例化 ?- list_compressed([friends(mike,4), friends(joe,3), 朋友(麦克,1),朋友(麦克,2), 朋友(乔,4),朋友(迈克,3)],Xs)。 Xs = [朋友(迈克,10),朋友(乔,7)]; 错误的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-30
      • 2017-05-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多