Missed opportunity
This doesn't run in reverse, doing run-length decoding!?!
That's just crazy.
?- clumped(X,[a-2,b-1,a-4,c-3]). false.
angry_coffee_slurp.jpg
Not "clumped" but "counted"
The counterpart of "clumped" is "counted": getting the occurrence count of atomic terms:
This one is based on SWI-Prolog dicts (which is "output" directly as "CountsDict". One could alternatively transform it into or even workd directly with a list of pairs.
% === % "Items" is a list of atomic (and thus ground) terms % that will appear as keys in an SWI-Prolog dict. % % "CountsDict" is an SWI-Prolog dict mapping every % "Item" encountered in "Items" to its occurrence count. % The tag of "CountsDict" is (unified with) "Tag". % % counted(+Items,-CountsDict,+Tag) % === counted(Items,CountsDict,Tag) :- dict_create(EmptyDict,Tag,[]), foldl(inc_for_key,Items,EmptyDict,CountsDict). % --- % Private helper % --- inc_for_key(Item,DictIn,DictOut) :- assertion(atomic(Item)), % lookup in a dict works via unification of atomic terms; make sure it's atomic (get_dict(Item,DictIn,Count) % how efficient is lookup in a dict (is it a linear scan for an unifiying element?) -> succ(Count,CountNext) ; CountNext=1), put_dict(Item,DictIn,CountNext,DictOut). % === % Tests % === :- begin_tests(counted). test("count empty list",true(Result == counts{})) :- counted([],Result,counts). test("#1",true(Result == foo{a:4,b:2,c:3,d:1,e:1,f:1})) :- counted([a,b,c,d,c,e,b,a,a,f,a,c],Result,foo). test("#2",true(Result == foo{' ':1,!:1,',':1,'H':1,'W':1,d:1,e:1,l:3,o:2,r:1})) :- atom_chars("Hello, World!",Chars), counted(Chars,Result,foo). :- end_tests(counted).