带数量的Prolog压缩列表-重复的答案

时间:2019-02-10 10:53:14

标签: list prolog compression

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

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

4 个答案:

答案 0 :(得分:5)

另一种方法是使用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 :(得分:3)

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

您正在做的事情太四舍五入而没有任何明显的好处(但是如果我错了,请纠正我)。

一种惯用的方法是使用M01/02/2019|F04:00:00|H154.4|L153.01|O154.4|T154|V3257进行排序而不删除重复项。这将使您需要汇总的条目彼此相邻。这样算起来就容易多了。

如果您还使用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

您可以在SWI-Prolog的源代码中查找?- 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/2的定义。

答案 2 :(得分:3)

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

....
....
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如果列表中有重复项,则成功不止一次,但您仅打算使用第一个结果。

答案 3 :(得分:3)

如果您更改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可能会给出虚假答案:

?- compress([friends(mike,4), friends(joe,3), friends(Any,10)], Xs).
Any = mike, Xs = [friends(mike,14),friends(joe,3)] ;
false.                             % what?! how about Any = joe?

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

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

示例查询:

?- list_compressed([friends(mike,4), friends(joe,3), friends(Any,10)], Xs).
ERROR: Arguments are not sufficiently instantiated

?- list_compressed([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.