我是 Prolog 的新手。
我需要帮助编写一个谓词来查找和删除列表中的最小元素。
非常感谢你!
如果所有列表项都是整数,我们可以使用clpfd!
:- use_module(library(clpfd)).
我们zmin_deleted/2
使用maplist/2
、(#=<)/2
、
same_length/2
和定义select/3
:
zmin_deleted(Zs1,Zs0) :-
same_length(Zs1,[_|Zs0]),
maplist(#=<(Min),Zs1),
select(Min,Zs1,Zs0).
示例查询:
?- zmin_deleted([3,2,7,8],Zs).
Zs = [3,7,8]
; false.
?- zmin_deleted([3,2,7,8,2],Zs).
Zs = [3, 7,8,2]
; Zs = [3,2,7,8 ]
; false.
请注意,这zmin_deleted/2
也适用于“其他方向”:
?- zmin_deleted(Zs,[3,2,7,8]).
_A in inf..2, Zs = [_A, 3, 2, 7, 8]
; _A in inf..2, Zs = [ 3,_A, 2, 7, 8]
; _A in inf..2, Zs = [ 3, 2,_A, 7, 8]
; _A in inf..2, Zs = [ 3, 2, 7,_A, 8]
; _A in inf..2, Zs = [ 3, 2, 7, 8,_A]
; false.
让我为你谷歌它。
无论如何,有一个很好的min_list
谓词。
?- min_list([1,2,2,3],X).
X = 1.
这是一个小例子,如何从列表中删除一些元素(注意,所有2
s 都消失了):
?- delete([1,2,2,3],2,X).
X = [1, 3].
如果您只想删除元素的第一次,请使用select
:
?- select(2, [2,1,2,2,3], X), !.
X = [1, 2, 2, 3].
所以你的最终答案可能是这样的:
delete_min(A, C) :-
min_list(A, B),
select(B, A, C), !.
和
?- delete_min([1,1,2,3],X).
X = [1, 2, 3].
同样,只需在列表上使用结构递归。列表由节点构建,[H|T]
即具有两个字段的复合数据结构- head和tail。Head 是节点中保存的数据(列表元素),是列表的其余部分。H
T
T
我们通过滚动列表找到最小元素,同时保留一个额外的数据 - 到目前为止所有看到的最小元素:
minimum_elt([H|T],X):- minimum_elt(T,H,X).
空列表情况没有定义 - 空列表没有最少元素。
minimum_elt([],X,X).
如果列表中没有更多元素,那么我们目前拥有的就是答案。
minimum_elt([A|B],M,X):-
这里有两种情况:A < M
或其他情况:
A < M, minimum_elt(B,A,X).
minimum_elt([A|B],M,X):-
A >= M, minimum_elt(B,M,X).
没什么好说的了,程序到此结束。
编辑:除了,您还想删除该元素。这改变了事情。唔。一种明显的方法是首先找到最小 elt,然后将其删除。我们将不得不再次比较所有元素,这次与之前找到的最小元素进行比较。我们可以在一次扫描中做到这一点吗?
在 Lisp 中我们可以。要从列表中删除任何元素,我们只需将前一个节点的尾指针重置为指向被删除节点之后的下一个节点。然后使用这种方法,我们将扫描输入列表一次,保持对前一个节点的引用为目前找到的最小元素,当我们找到越来越小的元素时交换它。然后,当我们到达列表的末尾时,我们只需通过手术移除最小节点。
但是在 Prolog 中,我们不能重置东西。Prolog 是一种设置一次的语言。所以看起来我们被困在需要遍历列表两次......或者我们可以尝试非常聪明,并在我们进行时构建所有可能的列表,每次找到新的候选者时将它们整理出来最小的元素。
rem_min([A|B],L):-
% two possibilities: A is or isn't the minimum elt
rem_min(B,A,([A|Z],Z,Z),L).
rem_min([],A,(With,Without,Tail),L):- Tail = [],
% A is indeed the minimal element
L = Without.
rem_min([H|T],A,(With,Without,Tail),L):- H >= A, Tail=[H|Z],
rem_min(T,A,(With,Without,Z),L).
rem_min([H|T],A,(With,Without,Tail),L):- H < A, % use this H
copy_the_list(With,Tail,W2,T2), % no good - quadratic behaviour
Tail=[H|Z], T2=Z,
rem_min(T,A,(With,W2,Z),L).
copy_the_list([A|B],T,[A|C],C):- var(B), !, T=B. % fresh tail
copy_the_list([A|B],T,[A|C],T2):- copy_the_list(B,T,C,T2).
所以看起来我们无法避免第二遍,但至少我们可以保存所有多余的比较:
rem_min([A|B],L):- N=[A|_], rem_min(B,A,N,[N|Z],Z,L).
rem_min([],_A,N,L2,Z,L):- Z=[], N=[_,1], % finalize the minimal entry
scan(L2,L).
rem_min([H|T],A,N,L2,Z,L):- H >= A, Z=[H|Z2], rem_min(T,A,N,L2,Z2,L).
rem_min([H|T],A,N,L2,Z,L):- H < A, % use this H
N2=[H|_], N=[_], % finalize the non-minimal entry
Z=[N2|Z2], rem_min(T,H,N2,L2,Z2,L).
scan( [], []).
scan( [[_,1]|B],C):- !, scan(B,C). % step over the minimal element
scan( [[A]|B],[A|C]):- !, scan(B,C). % previous candidate
scan( [A|B], [A|C]):- !, scan(B,C).