CPSC 322 - Lecture 11 - October 1, 2004

CPSC 322 - Lecture 11

Append: It Slices, It Dices, It Concatenates


I.  The Swiss Army Knife of CILOG predicates

Even though I put the text about append in the notes for the
previous lecture in case it proved to helpful in doing your
homework, we didn't really talk about it class until today.
So below are those notes repeated.  In addition, we talked
about how some of the homework problems could have been solved
with append, and those solutions show up in the posted solutions
for assignment 2.

The purpose of append is to join two lists together.  More 
accurately, the purpose is to prove that one particular list is
the result of joining two other specific lists together.
For example, we might want to prove:

  append([a,b],[c,d],[a,b,c,d]).

To find out the result of appending [a,b] and [c,d], we'd use
the more general form of the theorem:

  append([a,b],[c,d],X).

How do we look at this problem?  Don't be discouraged if the 
answer isn't obvious.  Defining append always seems to require
some breakthrough insight.  The insight here is that

  append([a,b],[c,d],[a,b,c,d])

is true if

  append([b],[c,d],[b,c,d]) 

is true (and a=a, which is true of course).  This is true if

  append([],[c,d],[c,d])

is true (and b=b, which is also true).  Is there any need to 
go further?  Nah.  So the generalized base case looks like this:

  append([],X,X).

Those reductions and tests of equality can be encoded like this:

  append([Head|Rest1], List2, [Head|Rest3]) <-
  append(Rest1, List2, Rest3).

The utility of append comes from its ability to split lists by
partially specifying the nature of the split lists.  For example,
you can find the possible front parts of a list by specifying just 
the back part of the list:

cilog: ask append(X,[c,d],Y).
Answer: append([], [c, d], [c, d]).
 Runtime since last report: 0 secs.
  [ok,more,how,help]: more.
Answer: append([A], [c, d], [A, c, d]).
 Runtime since last report: 0 secs.
  [ok,more,how,help]: more.
Answer: append([A, B], [c, d], [A, B, c, d]).
 Runtime since last report: 0 secs.
  [ok,more,how,help]: more.
Answer: append([A, B, C], [c, d], [A, B, C, c, d]).
 Runtime since last report: 0 secs.
  [ok,more,how,help]: 

Or you can find all the different ways to split one list into two:

cilog: ask append(X,Y,[a,b,c]).
Answer: append([], [a, b, c], [a, b, c]).
 Runtime since last report: 0 secs.
  [ok,more,how,help]: more.
Answer: append([a], [b, c], [a, b, c]).
 Runtime since last report: 0 secs.
  [ok,more,how,help]: more.
Answer: append([a, b], [c], [a, b, c]).
 Runtime since last report: 0 secs.
  [ok,more,how,help]: more.
Answer: append([a, b, c], [], [a, b, c]).
 Runtime since last report: 0 secs.
  [ok,more,how,help]: more.
No more answers.
 Runtime since last report: 0 secs.
cilog: 

Furthermore, lots of other problems can be viewed as instances of
list splitting, and the proof procedures associated with them can
be defined in terms of append:

  prefix(X,Y) <- append(X,M,Y)

Essentially, this says that X is a prefix of Y if there's some
mystery list M that can be appended to the back of X to produce Y.
The suffix procedure can be redefined in terms of append too:

  suffix(X,Y) <- append(M,X,Y)

In other words, X is a suffix of Y if there's some list M that can
be appended to the front of X to produce Y.

The member predicate can be viewed as list splitting:

  member(X,Y) <- append(M,[X|Rest],Y)

Which can be read as X is a member of Y if there's some list M
that can be appended to the front of another list whose first 
element is X, and the result of the appending is the list Y.

Last revised: October 3, 2004