Reputation: 217
I want to write a predicate which will determine if a list is sorted or not (ascending or descending both would return true)
is_sorted([]).
is_sorted([_]).
is_sorted([X,Y|T]):-X=<Y, is_sorted([Y|T]).
This works when I want to check only one particular order, how can I use it for both ascending and descending?
Upvotes: 1
Views: 1554
Reputation: 74197
To determine if a list is sorted in ascending order is trivial:
ordered_ascending( [] ) . % the empty list is ordered in ascending sequence.
ordered_ascending( [X] ) . % A list of length 1 is ordered in ascending sequence.
ordered_ascending( [A,B|Cs] ) :- % A list of length > 1 is ordered if...
A @=< B , % A is less than or equal to B, and
ordered_ascending( [B|Cs] ) . % the remainder of the list (less A) is similarly ordered.
Similarly, determining if a list is ordered in descending sequence is just as easy:
ordered_descending( [] ) . % the empty list is ordered in ascending sequence.
ordered_descending( [X] ) . % A list of length 1 is ordered in ascending sequence.
ordered_descending( [A,B|Cs] ) :- % A list of length > 1 is ordered if...
A @>= B , % A is greater than or equal to B, and
ordered_descending( [B|Cs] ) . % the remainder of the list (less A) is similarly ordered.
Add a wrapper to provide the needed determinism and you are all set:
ordered( Xs ) :- ordered_ascending( Xs ) , ! .
ordered( Xs ) :- ordered_descending( Xs ) , ! .
You can make it more efficient, by adding some smarts to the wrapper:
ordered( [] ) .
ordered( [X] ) .
ordered( [X,Y|Zs] ) :- X @=< Y , ordered_ascending( [Y|Zs] ) , ! .
ordered( [X,Y|Zs] ) :- X @>= Y , ordered_descending( [Y|Zs] ) , ! .
You'll notice that I discard the 1st element of the list here, as we don't need to test it again.
Upvotes: 0
Reputation: 10234
It is usually a good idea to look for solutions that do not require unification of more than the head and the tail of a list. That removes the need to use cuts in order to avoid leaving choice points open during evaluation.
sorted([]).
sorted([H|T]) :- sorted(T, H, _).
sorted([], _, _).
sorted([H|T], Last, Dir) :-
compare(Dir1, H, Last),
( Dir1 = (=)
-> true
; Dir1 = Dir),
sorted(T, H, Dir).
Upvotes: 1
Reputation: 5509
You can use compare/3
to instantiate the order (ascending or descending), as soon as the first pair of distinct adjacent elements is found in the list.
sorted(L) :- sorted(L, _).
sorted([], _).
sorted([_], _) :- !.
sorted([X,X|R], Order) :- !, sorted([X|R], Order).
sorted([X,Y|R], Order) :- compare(Order, X, Y), sorted([Y|R], Order).
Examples:
?- sorted([ann, bob, coy]).
true.
?- sorted([coy, bob, bob, ann]).
true.
?- sorted([coy, bob, dan, bob, ann]).
false.
?- sorted([1, 2, 3, 3, 3, 4]).
true.
?- sorted([4, 3, 2, 1]).
true.
?- sorted([1-coy, 2-ann, 3-bob]).
true.
?- sorted([5-coy, 4-ann, 3-bob]).
true.
?- sorted([1-coy, 4-ann, 3-bob]).
false.
Upvotes: 3
Reputation: 3753
You can pass the order predicate to is_sorted
.
is_sorted(_, []).
is_sorted(_, [_]).
is_sorted(P, [X,Y|T]):- call(P, X, Y), is_sorted(P, [Y|T]).
Here P
can be any predicate which takes two arguments.
?- is_sorted(=<, [1, 2, 3, 4, 4]).
true
?- is_sorted(<, [1, 2, 3, 4, 4]).
false.
?- is_sorted(>, [4, 3, 1, 0, -2]).
true
?- is_sorted(@<, [hey, how]).
true
?- is_sorted(@<, [hey, how, are]).
false.
To check if the list is sorted in either ascending or descending order:
sorted_any([]).
sorted_any([_]).
sorted_any([X, Y | T]) :-
(X = Y) -> sorted_any([Y|T]);
(
(X < Y) -> is_sorted(=<, [Y|T]);
is_sorted(>=, [Y|T])
).
Upvotes: 1