What is the effect of "!"
Reducing reduces the search space. That is, in another, clean and monotonous program, the cut will delete some decisions or answers. While this is redundant, that’s good. It sounds so innocent and helpful, right? Let's get a look!
And so I don’t forget, using [E,Nr] to indicate pairs is pretty unusual, it's better to use an E-Nr pair.
Now we compare counter_cut/2 and counter_sans/2 .
| ?- counter_cut([a,a],Xs). Xs = [[a,2]]. | ?- counter_sans([a,a],Xs). Xs = [[a, 2]] ; Xs = [[a, 1], [a, 1]]. % <<< surprise !!!
Thus, the cut version has fewer solutions. It seems that counter_cut/2 solution remains correct. In this very specific case. Will it always be right? I will try a minimally more general query:
| ?- counter_cut([a,B],Xs). B = a, Xs = [[a, 2]]. | ?- counter_sans([a,B],Xs). B = a, Xs = [[a, 2]] ; Xs = [[a, 1], [B, 1]].
Again, _sans is more frequent, and this time he is even a little right; the last answer includes B = b . In other words,
| ?- counter_cut([a,B], Xs), B = b. fails. % incomplete ! | ?- counter_sans([a,B], Xs), B = b. B = b, Xs = [[a,1],[b,1]].
So sometimes the _cut version _cut better, and sometimes the _sans . Or put it more bluntly: both are wrong, but the _sans version at least includes all the solutions.
Here is a “cleaned” version that simply rewrites the last rule in two different cases: one for the end of the list, and the other for the other, another element.
counter_pure([],[]). counter_pure([H|T],[[H,C1]|R]) :- counter_pure(T,[[H,C]|R]), C1 is C+1. counter_pure([H],[[H,1]]). counter_pure([H,D|T],[[H,1]|R]) :- dif(H,D), counter_pure([D|T],R).
In terms of efficiency, which is not too well known.
Here is a test case of efficiency for a system with efficient tree merging:
?- Es = [e|Es], counter(Es, Dict). resource_error(stack).
Instead, implementation should go smoothly, at least until the end of this universe. Strictly speaking, this query should cause a resource error, but only after it has counted a number much larger than 10^100000000 .