统计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, _) = elem(temp, val1)
elem(temp, _) = elem(temp, val2)
而且,我的问题是:
- Q1。为什么它不起作用?
- Q2。我怎样才能让它适用于任何类型?
谢谢。
您的代码无效,因为 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).
我有适用于数字的代码,但不适用于对象类型。
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, _) = elem(temp, val1)
elem(temp, _) = elem(temp, val2)
而且,我的问题是:
- Q1。为什么它不起作用?
- Q2。我怎样才能让它适用于任何类型?
谢谢。
您的代码无效,因为 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).