2

Right now I am stuck trying to define a predicate ascending/1 that takes a list of lists and determines if the lists are sorted within the list of lists in increasing size. I made a helper predicate smaller/2 and takes in 2 lists, for example A and B' and will succeed if A is less than or equal to B in length and fail otherwise. What my question is, is how do I recursively call smaller/2 within the predicate ascending/1 assuming that smaller/2 is implemented correctly? Im new to prolog so the syntax gets me confused. So far I have:

%empty list%

ascending([]).
ascending([Z]) :- smaller ([_|Z]), [_,_|Z]).

im trying to take the head of the list of lists and compare it to the second head of the list of lists. also I do not want to use any other predefined functions only my helper predicate.

false
  • 10,264
  • 13
  • 101
  • 209
Frank
  • 21
  • 2
  • If `sort/1` should succeed for sorted lists, why not call it `sorted/1` or even better: `ascending/1`? – false Nov 03 '14 at 17:09
  • I could, it was just a name I have given it though – Frank Nov 03 '14 at 17:13
  • 2
    Names influence what you think - and here in Prolog you really have to think differently. Thus a misleading name might influence whether or not you will understand Prolog. – false Nov 03 '14 at 17:15
  • Very true, I will change it. On the other hand, is the way I am using smaller correct? – Frank Nov 03 '14 at 17:18
  • ok so use that instead of smaller? – Frank Nov 03 '14 at 17:26
  • so my question is how to I extract the first and second list out of the list of lists for comparison? and then how do I move from the second to third lists and so on? – Frank Nov 03 '14 at 17:30
  • @Frank, You do realize that `[Z]` is a list of only one element, right? – lurker Nov 03 '14 at 18:44
  • Actually no i didn't know that. How would you represent a list of lists? – Frank Nov 03 '14 at 20:02

1 Answers1

2

So, here is my answer - a related question came up in the meantime.

Before answering your original question, I will look at ascending lists of integers. The reason: It's just simpler to distinguish here between the list and its elements.

Find a descriptive name

Avoid imperatives, since (pure) Prolog predicates do nothing. Or at best they describe relations. But they don't sort or add or compare. In our case ascending/1 might be an ideal name, sorted/1 is less ideal since it somewhat suggests that somebody will sort things. So there is still a tiny imp (imperative entity) around.

Give some test cases

This will often help to clarify the meaning to you. And always remember: Now, your mind is fresh, so put an effort to add meaningful test cases. Later you will become tired and less attentive, but let me assure you: Your Prolog system will never get tired. So you can put those test cases at the end of your file:

?- ascending([1,2]).
?- ascending([1,1,2]).
?- \+ ascending([2,1]).

Start with a predicate that is too general

A (trivial) first try might be:

ascending(_).

This definition succeeds always! Clearly too general. So how can we specialize it? Adding a single goal will not work. But since it should be a list - we can start with the definition of a list.

ascending([]).
ascending([_E|Es]) :-
   ascending(Es).

Now, this is still to general, we want a list of integer with certain properties. So again we need to specialize this definition. What we want is that for Es = [A,B,C] the goals A #=< B, B #=< C hold.

:- use_module(library(clpfd)).
ascending([]).
ascending([E|Es]) :-
   E #=< F,
   ascending(Es).

Did you see that warning about F being singleton? Prolog hates such variables, too often they are typos. And in a sense Prolog is right. We should relate F to something else. It should be the next element in Es.

ascending([]).
ascending([E|Es]) :-
   lesseq_than_first(E, Es),
   ascending(Es).

lesseq_than_first(_, []).
lesseq_than_first(E, [F|_]) :-
   E #=< F.

We could "optimize" this definition. But this form better reveals what it is about: It is a list, were elements are interconnected.

So much for ascending lists of integers. But, how essential was the fact that the list was ascending, couldn't a descending list almost possess the same definition? Isn't this a waste of our precious attention if too many names fill up our overloaded mind?

We had essentially a chain of values that were interconnected with (#=<)/2 which could be any other relation. So instead of ascending([1,2,3]) we could have said chain(#=<, [1,2,3]). (BTW: Is this a nice name? Or do you have a better one?)

chain(R_2, []).
chain(R_2, [E|Es]) :-
   rel_to_first(R_2, E, Es),
   chain(R_2, Es).

rel_to_first(_R_2, _E, []).
rel_to_first(R_2, E, [F|_]) :-
   call(R_2, E, F).

With this general definition, we can now go back to your original predicate: smaller/2 is not an ideal name. Maybe shorter_than/2?

shorter_than([], _).
shorter_than([_|Es], [_|Fs]) :-
   shorter_than(Es, Fs).

sorted(Ess) :-
  chain(shorter_than, Ess).

After having written this I realize that I forgot to illustrate another good advise: Play with the top-level! But this will not fit into this answer...

Community
  • 1
  • 1
false
  • 10,264
  • 13
  • 101
  • 209