“!”的作用是什么?
切割修剪搜索空间。也就是说,在一个纯粹且单调的程序中,剪切将删除一些解决方案或答案。只要那些是多余的就可以了。这听起来很无辜和有用,不是吗?我们来看一下!
以免我忘记,使用[E,Nr]
来表示对是相当不寻常的,最好使用对E-Nr
。
我们现在将比较counter_cut/2
和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 !!!
所以剪辑版的解法较少。似乎counter_cut/2
保留的解决方案是正确的。在这个非常特殊的情况下。它总是选对的吗?我将尝试一个更通用的查询:
| ?- 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]].
再次,_sans
更健谈,这一次,它甚至更正确;最后一个答案包括B = b
. 换句话说,
| ?- counter_cut([a,B], Xs), B = b.
fails. % incomplete !
| ?- counter_sans([a,B], Xs), B = b.
B = b,
Xs = [[a,1],[b,1]].
所以有时_cut
版本更好,有时_sans
. 或者更直接地说:两者都是错误的,但_sans
-version 至少包括所有解决方案。
这是一个“纯化”版本,它只是将最后一条规则重写为两种不同的情况:一种用于列表的末尾,另一种用于更进一步的不同元素。
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).
从不太著名的效率角度来看。
这是一个具有合理树统一的系统的效率测试用例:
?- Es = [e|Es], counter(Es, Dict).
resource_error(stack).
相反,实现应该平稳循环,至少到这个宇宙的尽头。严格来说,该查询必须产生资源错误,但只有在它计数到比10^100000000
.