可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I've been given the question:
Define a predicate ordered/1, which checks if a list of integers is correctly in ascending order. For example, the goal ordered([1,3,7,11])
should succeed, as should the goal ordered([1,3,3,7])
, whereas the goal ordered([1,7,3,9])
should fail.
So far I have this:
ordered([]).
ordered([N, M|Ns]):-
append(M, Ns, Tail),
ordered(Tail),
N =< M.
But it fails on every list.
I have deduced that the reason it fails is because it reaches the end number in the list then tries to compare that number against an empty list. Obviously this fails because you can't compare an integer to an empty list. Even if you could and it, say, returned 0
for an empty list, it would still return false as the number would be greater than 0
, not less than.
I can't find a solution... Any ideas? Thanks, Jon.
Edit
So, some slightly amended code:
ordered([]).
ordered([N]):-
N >= 0.
ordered([N, M|Ns]):-
append(M, Ns, Tail),
ordered(Tail),
N =< M.
This now works for ordered([1])
, but bigger lists still don't run correctly.
Should I include something like ordered([N, M|Ns])
in the definition?
回答1:
(assuming this is homework, I hesitate to give a complete solution).
Looking at your code, try to find out how it would unify ?- ordered([1]).
Run this query mentally (or using trace/0) and see what it does, step by step, and how it computes its result.
Also, please try to get "returns a value" out of your mind when thinking prolog. Prolog predicates don't return anything.
回答2:
I think your solution is not also tail-recursion-friendly.
Think something like that would do:
ordered([]) :-!.
ordered([_]):-!.
ordered([A,B|T]) :-
A =< B,
!,
ordered([B|T]).
回答3:
If you are using SICStus Prolog,
my previous answer will not work, as the
clpfd library in SICStus Prolog
does not offer the library predicate
chain/3
included with
SWI-Prolog's clpfd library.
:- use_module(library(clpfd)).
:- assert(clpfd:full_answer).
Don't panic! Simply implement predicate ordered/1
like this:
ordered([]).
ordered([X|Xs]) :-
ordered_prev(Xs,X).
ordered_prev([] ,_ ).
ordered_prev([X1|Xs],X0) :-
X0 #=< X1,
ordered_prev(Xs,X1).
Let's see it in action with SICStus Prolog 4.3.2.
Here's the most general query:
?- ordered(Xs).
Xs = []
; Xs = [_A]
; Xs = [_A,_B], _A#=<_B, _A in inf..sup, _B in inf..sup
; Xs = [_A,_B,_C], _A#=<_B, _B#=<_C, _A in inf..sup, _B in inf..sup, _C in inf..sup
... % an infinity of solutions follows: omitted for the sake of brevity.
And here are the queries the OP suggested:
?- ordered([1,3,7,11]).
yes % succeeds deterministically
?- ordered([1,3,3,7]).
yes % succeeds deterministically
?- ordered([1,7,3,9]).
no
Note that both succeeding queries in above example did not leave any useless choicepoints behind, thanks to first argument indexing.
回答4:
If your Prolog system supports clpfd, check if it offers the library predicate clpfd:chain/2
.
:- use_module(library(clpfd)).
If so, simply write:
?- chain([1,3,7,11],#<).
true.
?- chain([1,3,3,7],#=<).
true.
?- chain([1,3,3,7],#<).
false.
?- chain([1,7,3,9],#<).
false.
回答5:
You're quite right: according to your code there are only two possible ways a list can be ordered
:
- It's empty
- The first two items are in the correct order, and the rest of the list is
ordered
Those are certainly both correct statements, but what about the list [3]
? Isn't that ordered
too? Obviously a list with only one element is ordered, yet you have no provision for expressing that: it fits neither your base case nor your recursive case.
The single-element list is another case hiding here that you haven't addressed yet. Since this is independent of the two rules you've already defined, you might want to consider a way to address this special case separately.
回答6:
Well that, in the end, was rediculously easy to fix.
Here is the correct code.
ordered([]).
ordered([N, M|Ns]):-
append([M], Ns, Tail),
ordered(Tail),
N =< M.
ordered([M]).
ordered([M]). deals with the single-element list as described above.
The real root of my problem was not including [] around the M in the append function.
Whats the ettiquette regarding awarding the correct answer? You've both helped muchly.
Jon
回答7:
Don't use append/3
.
edit1 to satisfy @false. In order to make it tail recursive friendly it has to eliminate backtracking. This is tail-recursive and only slight variation on @Xonix:
ordered([X|[]]):-!.
ordered([X,Y|Ys]) :-
X =< Y,
!,
ordered([Y|Ys]).
edit2 Take it a step further to eliminate lists that have less than two elements
ordered([X,Y|[]]):- X =< Y,!.
ordered([X,Y|Ys]) :-
X =< Y,
!,
ordered([Y|Ys]).