3

I am new to Prolog.

I need help writing a predicate which finds and deletes the minimum element in a list.

Thank you very much!

repeat
  • 18,496
  • 4
  • 54
  • 166
user1913592
  • 165
  • 1
  • 9

3 Answers3

2

If all list items are integers, we can use !

:- use_module(library(clpfd)).

We define zmin_deleted/2 using maplist/2, (#=<)/2, same_length/2, and select/3:

zmin_deleted(Zs1,Zs0) :-
   same_length(Zs1,[_|Zs0]),
   maplist(#=<(Min),Zs1),
   select(Min,Zs1,Zs0).

Sample queries:

?- 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.

Note that zmin_deleted/2 also works in the "other direction":

?- 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.
Community
  • 1
  • 1
repeat
  • 18,496
  • 4
  • 54
  • 166
1

Let me google it for you.

How could you find a minimum of a list..

Anyway, there is a nice min_list predicate.

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

Here is a little example how could you remove some element from a list (notice, that all 2s is gone):

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

If you wanna remove only first occasion of a element, use select:

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

So your final answer could be something like that:

delete_min(A, C) :-
  min_list(A, B),
  select(B, A, C), !.

And

?- delete_min([1,1,2,3],X).
X = [1, 2, 3].
Community
  • 1
  • 1
0

Again, just use the structural recursion on lists. Lists are built of nodes, [H|T], i.e. compound data structures with two fields - head, H, and tail, T. Head is the data (list element) held in the node, and T is the rest of the list.

We find minimum element by rolling along the list, while keeping an additional data - the minimum element from all seen so far:

minimum_elt([H|T],X):- minimum_elt(T,H,X).

There is no definition for the empty list case - empty lists do not have minimum elements.

minimum_elt([],X,X).

If there are no more elements in the list, the one we have so far is the answer.

minimum_elt([A|B],M,X):- 

two cases here: A < M, or otherwise:

    A < M, minimum_elt(B,A,X).

minimum_elt([A|B],M,X):- 

    A >= M, minimum_elt(B,M,X).

Nothing more to say about it, so this completes the program.


edit: except, you wanted to also delete that element. This changes things. Hmm. One obvious approach is first find the minimum elt, then delete it. We'll have to compare all elements once again, this time against the minimal element found previously. Can we do this in one scan?

In Lisp we could have. To remove any element from a list, surgically, we just reset the tail pointer of a previous node to point to the next node after the one being removed. Then using this approach, we'd scan the input list once, keeping the reference to the previous node to the minimum element found so far, swapping it as we find the smaller and smaller elements. Then when we've reached the end of the list, we'd just surgically remove the minimal node.

But in Prolog, we can't reset things. Prolog is a set once language. So looks like we're stuck with the need to pass over the list twice ... or we can try to be very smart about it, and construct all possible lists as we go, sorting them out each time we find the new candidate for the smallest element.

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

So looks like we can't avoid the second pass, but at least we can save all the superfluous comparisons:

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).    
Will Ness
  • 70,110
  • 9
  • 98
  • 181