统计Prolog中特定对象元素的个数

Count the number of a specific object element in Prolog

我有适用于数字的代码,但不适用于对象类型。

count(_, [], 0).

count(Field, [Field|Tail], N) :-
  count(Field, Tail, N1),
  N is N1 + 1.

count(Field, [H|Tail], N) :-
  Field \= H,
  count(Field, Tail, N).
?- count(1, [1,2,3], X).
X = 1 . % ok

?- count(1, [1,2,3,1], X).
X = 2 . % ok

?- count(elem(temp, _), [elem(temp, val1), elem(temp, val2)], X).
X = 1 . % I expected 2 here.

对于最后一行,我预计 X = 2,因为

而且,我的问题是:

谢谢。

您的代码无效,因为 elem(temp,_) 与列表的第一项统一,并且该统一在整个递归调用中传播。您可以通过跟踪执行来观察这一点。

?- trace, count(elem(temp, _), [elem(temp, val1), elem(temp, val2)], X).
   Call: (11) count(elem(temp, _30894), [elem(temp, val1), elem(temp, val2)], _30926) ? creep
   Call: (12) count(elem(temp, val1), [elem(temp, val2)], _31518) ? creep
   Call: (13) elem(temp, val1)\=elem(temp, val2) ? creep
   Exit: (13) elem(temp, val1)\=elem(temp, val2) ? creep
   Call: (13) count(elem(temp, val1), [], _31650) ? creep
   Exit: (13) count(elem(temp, val1), [], 0) ? creep
   Exit: (12) count(elem(temp, val1), [elem(temp, val2)], 0) ? creep
   Call: (12) _30926 is 0+1 ? creep
   Exit: (12) 1 is 0+1 ? creep
   Exit: (11) count(elem(temp, val1), [elem(temp, val1), elem(temp, val2)], 1) ? creep
X = 1 .

要使其工作,您可以使用谓词 subsumes_term/2:

?- subsumes_term(1, 1).
true.

?- subsumes_term(1, 2).
false.

?- subsumes_term(elem(temp,_), elem(temp,val1)).
true.

?- subsumes_term(elem(temp,_), elem(temp,val2)).
true.

?- subsumes_term(elem(temp,val1), elem(temp,val2)).
false.

因此,一个可能的解决方案是:

count(_, [], 0).

count(Field, [First|Tail], N) :-
   subsumes_term(Field, First),
   count(Field, Tail, N1),
   N is N1 + 1.

count(Field, [First|Tail], N) :-
  not(subsumes_term(Field, First)),
  count(Field, Tail, N).

运行 例子:

?- count(1, [1,2,3], X).
X = 1 ;
false.

?- count(1, [1,2,3,1], X).
X = 2 ;
false.

?- count(elem(temp, _), [elem(temp, val1), elem(temp, val2)], X).
X = 2 ;
false.

一个更高效的版本是:

count(Field, List, Count) :-
   count(List, Field, 0, Count).

count([], _, Acc, Acc).

count([First|Tail], Field, Acc, Count) :-
   (   subsumes_term(Field, First)
   ->  Acc1 is Acc + 1
   ;   Acc1 is Acc ),
  count(Tail, Field, Acc1, Count).