Prolog:如何查找和删除列表中的最小元素?

3
我刚接触Prolog。
我需要帮助编写一个谓词,它可以在列表中查找并删除最小元素。
非常感谢!

1
关闭,因为不是一个真正的问题 <-- 完全不理解。 - ДМИТРИЙ МАЛИКОВ
@ДМИТРИЙМАЛИКОВ,不幸的是,人们很容易关闭他们不喜欢的问题。它可能被关闭是因为OP没有明显的研究迹象。 - Tyler Crompton
3个回答

2
如果所有列表项都是整数,我们可以使用
:- use_module(library(clpfd)).

我们使用maplist/2, (#=<)/2, same_length/2select/3定义zmin_deleted/2
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.

1

让我帮你谷歌一下。

如何找到列表的最小值。

无论如何,有一个很好的{{link2:min_list}}谓词。

?- min_list([1,2,2,3],X).
X = 1.

这里有一个小例子,展示如何从列表中删除某些元素(请注意,所有的2都被删除了):

?- delete([1,2,2,3],2,X).
X = [1, 3].

如果您想仅删除元素的第一个实例,请使用{{link1: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].

0

再次,只需在列表上使用结构递归。列表由节点 [H|T] 组成,即具有两个字段 - 头部 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).

关于此,没有更多可说的了,程序到此结束。


编辑:除非你还想删除那个元素。这改变了事情。一个明显的方法是先找到最小的元素,然后删除它。我们将不得不再次比较所有元素,这一次是与之前找到的最小元素进行比较。我们能在一次扫描中完成吗?

在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).    

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接