[英]Different ways of computing list length in Prolog
以下是 Prolog 中计算列表长度的 4 种不同方法:
:- use_module(library(clpz)).
list_length1([], 0).
list_length1([_|T], N) :-
N #> 0,
N1 #= N - 1,
list_length1(T, N1).
list_length2(A, N) :-
N #>= 0,
list_length2(A, 0, N).
list_length2([], N, N).
list_length2([_|T], I, N) :-
I #< N,
I1 #= I + 1,
list_length2(T, I1, N).
list_length3(A, N) :-
list_length3(A, 0, N).
list_length3([], N, N).
list_length3([_|T], I, N) :-
I1 is I + 1,
list_length3(T, I1, N).
list_length4([], 0).
list_length4([_|T], N) :-
list_length4(T, N0),
N is N0 + 1.
list_length1
- 数学简单,适用于所有方向list_length2
- 不知道这有什么用,通过类比创建list_length3
- 上次调用优化,仅在一个方向上工作list_length4
- 递归,单向工作 list_length2
或list_length4
有什么优点吗?
list_length2
- 不知道这有什么用,通过类比创建
为了更好地理解版本 1 和 2 之间的区别,请考虑对谓词进行概括。 粗略的概括。 只需添加事实list_length1(_,_).
和list_length2(_, _, _).
在他们的定义前面。 现在从声明的角度来看,这些定义变得毫无用处。 但是从程序的角度来看,我们现在得到了一些有用的提示:
?- list_length1("abc",N).
true
; clpz:(_A+1#=N), clpz:(N in 1..sup), clpz:(_A in 0..sup)
; clpz:(_A+1#=N), clpz:(_B+1#=_A), clpz:(N in 2..sup), clpz:(_A in 1..sup), clpz:(_B in 0..sup)
; clpz:(_A+1#=N), clpz:(_B+1#=_A), clpz:(_C+1#=_B), clpz:(N in 3..sup), clpz:(_A in 2..sup), clpz:(_B in 1..sup), clpz:(_C in 0..sup)
; N = 3.
?- list_length2("abc",N).
clpz:(N in 0..sup)
; clpz:(N in 1..sup)
; clpz:(N in 2..sup)
; clpz:(N in 3..sup)
; N = 3.
因此,版本 1 必须创建一系列微不足道的算术关系及其伴随的变量,这些变量与列表的长度成正比,这些变量将通过每次进一步的推理而更新,并且会在最后一刻崩溃,而版本 2 很满意单个域(在这种情况下是无限的)越来越受到每一步的约束。
(与I
相关的操作实际上都是微不足道(is)/2
计算。)
尝试?- list_length2(L,N), N = 10000.
查看性能方面的差异。
理论上,版本 1 可以避免创建这些琐碎的关系并更新单个变量以避免中间变量,但到目前为止我还没有看到能够做到这一点的系统。 即使有这样的系统,版本 1 仍然会比版本 2 慢,后者只对单个变量感到满意。
list_length4
有什么优点吗?
这个版本以非常昂贵、不必要的二次方方式产生答案。 对于list_length4(L, N)
的每个进一步答案,它需要与L
的长度成比例的时间。 尝试,例如?- list_length3(L,N), N = 10000.
并将其与版本 4 进行比较。
为了数学上的简单明了,请考虑在您的程序中使用(#)/1
。 所以代替N #>= 0
只需写#N #>= 0
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.