We ask here what (anonymous) list when appended to the list with X as its sole element would yield L and through this process get a binding for X. Another way to think of this is the following: suppose we want to define the relation last_element between an element and a list. Here is how we might do it:?- append(_,[X],L).
If you were not able to get this solution earlier but understand the comments here, try your hand again at Problem 11.2(e) from the book.last_element(X,L) :- append(_,[X],L).
Defining permutation should not be too difficult once we have select. Clearly the only permutation of the empty list is the empty list itself. Now, when is the list Y a permutation of the (non-empty) list X? Obviously when the first element of Y is some selected element of X and the rest of Y is a permutation of the remaining elements of X. Once you have reasoned this way, it is easy to write down the definition of permutation:
permutation([],[]). permutation(X,[E|Z]) :- select(E,X,Rest), permutation(Rest,Z).
If you understand the way things work in this case, it should not be too difficult to also construct a definition of mergelists. Incidentally, at least one student wondered what the difference was between mergelists and append. What mergelists requires is that the order of elements of each of the given lists be preserved. The predicate append imposes a stronger requirement: not only must the relative order within each list be preserved, but also every element of the first list must precede the elements of the second list. When looked at this way, we see that mergelists potentially has many more solutions than does append for the same lists given as input. This is also illustrated by one of the examples on the HyperNews page for hw8.
To represent a truth assignment, we might use a list of pairs of two components: a propositional variable and the truth value for it. Thus, the list [pr(pv("p"),ltrue), pr(pv("q"),lfalse)] might represent a truth assignment relevant to the logical expression whose encoding we have just described.
The definition of istrue should not be difficult to write: we have discussed how to define a predicate or function on recursively defined data a few times now. Assuming we have this definition in hand, then defining isnottaut is not difficult: we have to collect all the propositional variables in the given expression, generate one truth assignment after another for these variables till we find one that leads to a failure with istrue. Thus, we get the definition
isnottaut(F) :-
prop_vars(F,PVs),assignment(PVs,L),not(istrue(F,L)).
Think of defining prop_vars and assignment to complete
the definition of isnottaut. A hint for defining
assignment: you may find it useful to add the following clauses
to your program:
This predicate allows us to pick a truth assignment for any given propositional variable.assign_one(pv(PV),lfalse). assign_one(pv(PV),ltrue).
Once you have a definition of isnottaut you can define istaut in the obvious way:
istaut(F) :- not(isnottaut(F)).
While many of you got the general spirit of what is going on, very few of you really understood how to draw a search tree. This is described quite clearly in the book and you would do well to check this out before the exam.
For the first part of this problem, you had to define enumerate_interval and safe. The following could serve as a definition of the first predicate:
enumerate_interval(Low,Hi,[]) :- Low > Hi.
enumerate_interval(Low,Hi,[Low|Rest]) :-
Low =< Hi, N is Low + 1, enumerate_interval(N,Hi,Rest).
In defining this predicate, some of you wrote rather convoluted Prolog
code. For example, you wrote a definition that worked down from
Hi and used append to add this element at the end of the
list. This is quite inefficient as we specifically noted in
conjunction with naive reverse. We were somewhat generous in
grading in this case, this being the last homework. You should note,
however, that writing well-structured, readable and efficient
definitions is not an incidental especially in a programming lanuages
course. That some code "works" is often secondary to the other
attributes mentioned.
We will let you ponder the definition of safe and nqueens_aux (for the other solution to the N-Queens problem). Something to note regarding safe: the representation we have chosen for the board position already ensures that no two queens can be in the same column. Thus, it is not really necessary to check this property explicitly in the definition of safe.
Some of you are having difficulty with the list notation and you should practice this a bit to get the wrinkle out. For example, [X|[]] is a rather complicated way of writing [X]. There are a few more things of this sort we would have noted in the hard copy you turned in and you should make sure you understand the comments.
Last updated on May 10, 2006 by gopalan atsign cs.umn.edu and xqi atsign cs.umn.edu