我有一个元素列表,其中包含一个人拥有的朋友数量。
[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.
答案 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.