views:

323

answers:

4

Hi,

I've been given the question:

'Define a predcate ordered/1, which cheks if a list of integers is correctly in ascending order. For example, the goal ordered([1,3,7,11]) should succed, 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've been eyeballing this for a good hour or so and can't seem to find a solution. Any ideas?

Thanks

Jon

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. Am I just being stupid and should something like this be included in my 'ordered([N, M|Ns])' definintion?

+3  A: 

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

Martin v. Löwis
Not homework, but a past exam question. We have to define actual predicates in our exam. It's a bit rubbish as you have no way to test our solutions in the exam. Probably would get *some* marks for the above. While i'm practicing, however, I'm sat at the computer testing all my solutions and this one failed! :( Thanks for your help. I'll have another go at it.
JonB
Ok. When mentally tracing through a prolog predicate, take explicit note of how variables get bound/unified.
Martin v. Löwis
So running through my code for ordered([3]): N is 3, while M is [] or null. And the appending M to Ns (where Ns is []) results in Tail being []. So i either need to check for when M is [] or null. Can things be 'null' or are they always an empty list, [] ?
JonB
Right, well having worked out how to use trace, I've now realised my original code was wrong. I was missing a [] around the M when appending the list.
JonB
Actually, no. A single-element list just doesn't unify with [N, M|Ns] - so it's not that M is null (what value would Ns then have?). null and the empty list are really the same (actually, I think there is no proper "null").
Martin v. Löwis
+1  A: 

You're quite right: according to your code there are only two possible ways a list can be ordered:

  1. It's empty
  2. 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.

VoteyDisciple
have added some amended code for a single element list.
JonB
A: 

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

JonB
I think we both missed the point that you were also using append incorrectly, and focused on the single-element case. Not sure what the etiquette is - I'd pick the answer that is more formally correct wrt. the original question.
Martin v. Löwis
Since you didn't get any answers that ultimately solved the problem, accepting your own answer makes the most sense.
VoteyDisciple
+2  A: 

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