首先,我不认为提议的实现非常优雅:在这里,它们通过每次构造一个新列表来传递迄今为止找到的最小元素。使用附加参数(我们称为 accumulator)通常是可行的方法(并且可能也更有效)。
为了解决这个问题,我们首先必须找到一个整数。我们可以这样做:
sint([H|T],R) :-
integer(H),
!,
sint(T,H,R).
sint([_|T],R) :-
sint(T,R).
所以我们在这里检查 head H 是否是 integer/1。如果是这种情况,我们称谓词sint/3(不要与sint/2 混淆)。否则,我们使用列表的 tail T 递归调用 sint/2。
现在我们仍然需要定义sint/3。如果我们已经到达列表的末尾[],我们只需返回到目前为止找到的最小值:
sint([],R,R).
否则有两种情况:
-
head H 是一个整数,并且比目前找到的元素小,在这种情况下,我们将 head 作为新的当前最小值执行递归:
sint([H|T],M,R):
integer(H),
H < M,
!,
sint(T,H,R).
-
否则,我们只是忽略头部,并使用尾部T进行递归。
sint([_|T],M,R) :-
sint(T,M,R).
我们可以将递归子句放在 if-then-else 结构中。加上前面定义的谓词,完整的程序是:
sint([H|T],R) :-
integer(H),
!,
sint(T,H,R).
sint([_|T],R) :-
sint(T,R).
sint([],R,R).
sint([H|T],M,R):
(
(integer(H),H < M)
-> sint(T,H,R)
; sint(T,M,R)
).
这种方法的优点是过滤和比较(以获得最小值)是同时完成的,所以我们只在列表上迭代一次。这通常会导致性能提升,因为“控制结构”只执行一次:在迭代中完成更多工作,但我们只迭代一次。
我们可以通过使 filter 通用化来概括该方法:
filter_minimum(Filter,[H|T],R) :-
Goal =.. [Filter,H],
call(Goal),
!,
filter_minimum(Filter,T,H,R).
filter_minimum(Filter,[_|T],R) :-
filter_minimum(Filter,T,R).
filter_minimum(_,[],R,R).
filter_minimum(Filter,[H|T],M,R) :-
Goal =.. [Filter,H],
(
(call(Goal),H < M)
-> filter_minimum(Filter,T,H,R)
; filter_minimum(Filter,T,M,R)
).
然后你可以调用它:
filter_minimum(integer,[a,b,3,2,1],R).
用integer/1 过滤并计算最小值。