I'm trying to re-familiarize myself with Prolog and I thought this could be the type of problem with an elegant solution in Prolog.
I'm following along this example:
http://home.deib.polimi.it/matteucc/Clustering/tutorial_html/hierarchical.html
I've tried a variety of data formats:
dist('BA','FI',662).
dist(0,'BA','FI',662).
dist(['BA'],['FI'],662).
but I haven't found any particular one most suitable.
Here's all the data in the first format:
%% Graph distances
dist('BA','FI',662).
dist('BA','MI',877).
dist('BA','NA',255).
dist('BA','RM',412).
dist('BA','TO',996).
dist('FI','MI',295).
dist('FI','NA',468).
dist('FI','RM',268).
dist('FI','TO',400).
dist('MI','NA',754).
dist('MI','RM',564).
dist('MI','TO',138).
dist('NA','RM',219).
dist('NA','TO',869).
dist('RM','TO',669).
Now, there seems to be some awesome structure to this problem to exploit, but I'm really struggling to get a grasp of it. I think I've got the first cluster here (thought it may not be the most elegant way of doing it ;)
minDist(A,B,D) :- dist(A,B,D), dist(X,Y,Z), A \= X, A \= Y, B \= X, B \= Y, D < Z.
min(A,B,B) :- B < A
min(A,B,A) :- A < B
dist([A,B],C, D) :- minDist(A,B,D), dist(A,C,Q), dist(B,C,W), min(Q,W,D)
The problem I have here is the concept of "replacing" the dist statements involving A and B with the cluster.
This just quickly become a brainteaser for me and I'm stuck. Any ideas on how to formulate this? Or is this perhaps just not the kind of problem elegantly solved with Prolog?
Your table is actually perfect! The problem is that you don't have an intermediate data structure. I'm guessing you'll find the following code pretty surprising. In Prolog, you can simply use whatever structures you want, and it will actually work. First let's get the preliminary we need for calculating distance without regard for argument order:
distance(X, Y, Dist) :- dist(X, Y, Dist) ; dist(Y, X, Dist).
This just swaps the order if it doesn't get a distance on the first try.
Another utility we'll need: the list of cities:
all_cities(['BA','FI','MI','NA','RM','TO']).
This is just helpful; we could compute it, but it would be tedious and weird looking.
OK, so the end of the linked article makes it clear that what is actually being created is a tree structure. The article doesn't show you the tree at all until you get to the end, so it isn't obvious that's what's going on in the merges. In Prolog, we can simply use the structure we want and there it is, and it will work. To demonstrate, let's enumerate the items in a tree with something like member/2 for lists:
% Our clustering forms a tree. So we need to be able to do some basic
% operations on the tree, like get all of the cities in the tree. This
% predicate shows how that is done, and shows what the structure of
% the cluster is going to look like.
cluster_member(X, leaf(X)).
cluster_member(X, cluster(Left, Right)) :-
cluster_member(X, Left) ; cluster_member(X, Right).
So you can see we're going to be making use of trees using leaf('FI') for instance, to represent a leaf-node, a cluster of N=1, and cluster(X,Y) to represent a cluster tree with two branches. The code above lets you enumerate all the cities within a cluster, which we'll need to compute the minimum distance between them.
% To calculate the minimum distance between two cluster positions we
% need to basically pair up each city from each side of the cluster
% and find the minimum.
cluster_distance(X, Y, Distance) :-
setof(D,
XCity^YCity^(
cluster_member(XCity, X),
cluster_member(YCity, Y),
distance(XCity, YCity, D)),
[Distance|_]).
This probably looks pretty weird. I'm cheating here. The setof/3 metapredicate finds solutions for a particular goal. The calling pattern is something like setof(Template, Goal, Result) where the Result will become a list of Template for each Goal success. This is just like bagof/3 except that setof/3 gives you unique results. How does it do that? By sorting! My third argument is [Distance|_], saying just give me the first item in the result list. Because the result is sorted, the first item in the list will be the smallest. It's a big cheat!
The XCity^YCity^ notation says to setof/3: I don't care what these variables actually are. It marks them as "existential variables." This means Prolog will not provide multiple solutions for each city combination; they will all be thrown together and sorted once.
This is all we need to perform the clustering!
From the article, the base case is when you have two clusters left: just combine them:
% OK, the base case for clustering is that we have two items left, so
% we cluster them together.
cluster([Left,Right], cluster(Left,Right)).
The inductive case takes the list of results and finds the two which are nearest and combines them. Hold on!
% The inductive case is: pair up each cluster and find the minimum distance.
cluster(CityClusters, FinalCityClusters) :-
CityClusters = [_,_,_|_], % ensure we have >2 clusters
setof(result(D, cluster(N1,N2), CC2),
CC1^(select(N1, CityClusters, CC1),
select(N2, CC1, CC2),
cluster_distance(N1, N2, D)),
[result(_, NewCluster, Remainder)|_]),
cluster([NewCluster|Remainder], FinalCityClusters).
Prolog's built-in sorting is to sort a structure on the first argument. We cheat again here by creating a new structure, result/3, which will contain the distance, the cluster with that distance, and the remaining items to be considered. select/3 is extremely handy. It works by pulling an item out of the list and then giving you back the list without that item. We use it twice here to select two items from the list (I don't have to worry about comparing a place to itself as a result!). CC1 is marked as a free variable. The result structures will be created for considering each possible cluster with the items we were given. Again, setof/3 will sort the list to make it unique, so the first item in the list will happen to be the one with the shortest distance. It's a lot of work for one setof/3 call, but I like to cheat!
The last line says, take the new cluster and append it to the remaining items, and forward it on recursively to ourself. The result of that invocation will eventually be the base case.
Now does it work? Let's make a quick-n-dirty main procedure to test it:
main :-
setof(leaf(X), (all_cities(Cities), member(X, Cities)), Basis),
cluster(Basis, Result),
write(Result), nl.
Line one is a cheesy way to construct the initial conditions (all cities in their own cluster of one). Line two calls our predicate to cluster things. Then we write it out. What do we get? (Output manually indented for readability.)
cluster(
cluster(
leaf(FI),
cluster(
leaf(BA),
cluster(
leaf(NA),
leaf(RM)))),
cluster(
leaf(MI),
leaf(TO)))
The order is slightly different, but the result is the same!
If you're perplexed by my use of setof/3 (I would be!) then consider rewriting those predicates using the aggregate library or with simple recursive procedures that aggregate and find the minimum by hand.
Related
The goal is to select shapes that don't touch each other using constraints (clpfd). Calling start(Pairs,4) would return Pairs = [1,3,5,7].
One problem I noticed is that if I print Final before labeling, it prints [1,3,5,7]. Which means labeling isn't doing anything.
What could I change/add to this code in order to fix that and also remove possible backtracking?
:-use_module(library(clpfd)).
:-use_module(library(lists)).
% init initialises Pairs and Max
% Pairs - The elements inside the Nth list in Pairs,
% represent the index of the shapes that shape N can touch
init([[3,5,6,7],[4,5,7],[1,4,5,7],[2,3,7],[1,2,3,7],[1],[1,2,3,4,5]],7).
start(Final, N):-
init(Pairs, Max),
length(Final, N),
domain(Final, 1, Max),
ascending(Final),
all_different(Final),
rules(Pairs,Final),
labeling([],Final).
rules(_,[]).
rules(Pairs,[H|T]):-
nth1(H,Pairs,PairH),
secondrule(PairH,T),
rules(Pairs,T).
secondrule(_, []).
secondrule(PairH, [H|T]):-
element(_,PairH,H),
secondrule(PairH, T).
ascending([_|[]]).
ascending([H|[T1|T2]]):-
H #< T1,
ascending([T1|T2]).
This is an Independent Set problem, which is an NP-hard problem. Therefore, it is unlikely that anybody will ever find a way to do it without search (backtracking) for general instances.
Regarding your code, labeling/2 does nothing, because your rules/2 is in fact a search procedure that returns the solution it it can find it. all_different/1 is useless too, because it is implied by ascending/1.
Presumably, your goal is a program that sets up constraints (without any search) and then searches for a solution with labeling/2. For that, you need to rethink your constraint model. Read up a bit on independent sets.
I'm trying to write a predicate randomnames/1 that generates a random list of three different names. The names are in a database and I already have a predicate for one random name:
name(1, Mary).
name(2, Pat).
name(3, James).
name(4, Bob).
name(5, Susan).
random_name(Name):-
random(0, 6, N),
name(N, Name).
To get this in a list, someone suggested I should do:
random_names([A,B,C]) :-
random_name(A),
random_name(B),
random_name(C).
The only problem with this is that it's possible to get duplicates in the list generated. I can't figure out how to fix that issue. I could write a new predicate for removing duplicates, but how would I substitute the duplicate with another variable then, so that the list still has three elements? And how would I even write the remove predicate when there isn't a clear head and tail in the random_names predicate?
When programming in Prolog, think in terms of conditions that your solutions must satisfy.
Currently, you have already figured out how to describe a list with three elements, where each element is a random name.
That's a good start, but not yet sufficient: In addition, you now want to describe the condition that the elements are pairwise distinct.
So, think about how to describe a list of three elements where all elements are pairwise distinct.
I give you a start, using dif/2 to express disequality of terms in a sound way (see prolog-dif):
three_distinct_elements([A,B,C]) :-
dif(A, B),
dif(A, C),
dif(B, C).
You may find a more general and more elegant way to describe this for lists with more than 3 elements. However, the above suffices to solve the task at hand.
So, it only remains to combine the predicates you already have, using for example:
three_distinct_random_names(Ls) :-
random_names(Ls),
three_distinct_elements(Ls).
This is simply the conjunction of conditions which you have already implemented. In total, solutions of this predicate will give you what you want: A list with three distinct random names.
However, the predicate may also fail (Exercise: Why?).
To try the predicate until it finds a solution, use for example repeat/0:
?- repeat, three_distinct_random_names(Ls).
There are also better ways to solve this. However, as a first approximation, I recommend to focus on good building-blocks, describing the conditions you want to satisfy.
I have a general comment on what you write:
I could write a new predicate for removing duplicates, but how would I substitute the duplicate with another variable then, so that the list still has three elements?
This is all worded very imperatively: You think here about "removing", "substituting" etc. To get the most out of Prolog, focus on describing the conditions that must hold for the solutions you want to find!
You want to find a list without duplicates? Describe what such a a list must look like. You want random names? Describe what such a name looks like, etc.
In case if you are using Swi-Prolog you can use very handy randseq/3 predicate which comes bundled with Swi. randseq/3 generates list of all distinct random numbers in range from 1 to N. After getting this list generated all that remains is mapping numbers to names:
name(1, 'Mary').
name(2, 'Pat').
name(3, 'James').
name(4, 'Bob').
name(5, 'Susan').
random_names(Names, Count) :-
% 5 is the amount of names available in database
randseq(Count, 5, L),
maplist(name, L, Names).
Usage examples:
?- random_names(Names, 3).
Names = ['Mary', 'James', 'Susan'].
?- random_names(Names, 5).
Names = ['Susan', 'Bob', 'James', 'Mary', 'Pat'].
I need to append some elements from a list to another like this:
find_same(pt(1,1),pt(2,2),6,[slope(6,pt(3,3)),slope(6,pt(4,4)),slope(7,pt(3,2)),slope(9,pt(5,5))],NL).
result
NL=[pt(1,1),pt(2,2),pt(3,3),pt(4,4)]
I have tried using append but i have some problem, with this code:
find_same(_,_,_,[],_):-!.
find_same(pt(X,Y),pt(Xa,Ya),R,Slopes,Nl):-X\=a,
append(Nla,[pt(X,Y),pt(Xa,Ya)],Nl),
find_same(pt(a,a),pt(b,b),R,Slopes,Nla).
find_same(pt(X,Y),pt(Xa,Ya),R,[slope(R,pt(Xs,Ys))|Ss],Nl):-X=a,
append(Nla,[pt(Xs,Ys)],Nl),
find_same(pt(X,Y),pt(Xa,Ya),R,Ss,Nla).
find_same(_,_,R1,[slope(R2,_)|_],_):-R1\=R2,!.
because return me a lot of list.
then i tried with this other code:
find_same2(_,_,_,[],_):-!.
find_same2(pt(X,Y),pt(Xa,Ya),R,Slopes,_):-X\=a,
find_same2(pt(a,a),pt(b,b),R,Slopes,[pt(X,Y),pt(Xa,Ya)]).
find_same2(pt(X,Y),pt(Xa,Ya),R,[slope(R,pt(Xd,Yd))|Ss],[pt(Xd,Yd)|Nl]):-
X=a,!,
find_same2(pt(X,Y),pt(Xa,Ya),R,Ss,Nl).
find_same2(_,_,R1,[slope(R2,_)|_],_):-R1\=R2.
But it returns only false.
how can i solve this problem? thank you
Because you don't care what the specific components of the points are, you can treat each point as a single variable. So instead of pt(X, Y) for example, just consider it P.
So you want to find the common points:
find_common(P1, P2, N, SlopeList, Result)
If you can create a sublist of elements from SlopeList which meet your criteria, then you can form Result by prepending the two existing points. If that sublist is called, CommonPoints, then your Result would just be [P1, P2 | CommonPoints].
Now you only need to figure out how to determine CommonPoints. You can use member/2 to do this. Consider:
member(slope(N, P), SlopeList)
This will succeed for each element of SlopeList that has an element slope(N, P). To gather them together, you can use findall/3:
findall(P, member(slope(N, P), SlopeList), CommonPoints).
Those are all the pieces you need to solve your problem. You just have to put them together.
I'm in a bit of pickle in Prolog.
I have a collection of objects. These objects have a certain dimension, hence weight.
I want to split up these objects in 2 sets (which form the entire set together) in such a way that their difference in total weight is minimal.
The first thing I tried was the following (pseudo-code):
-> findall with predicate createSets(List, set(A, B))
-> iterate over results while
---> calculate weight of both
---> calculate difference
---> loop with current difference and compare to current difference
till end of list of sets
This is pretty straightforward. The issue here is that I have a list of +/- 30 objects. Creating all possible sets causes a stack overflow.
Helper predicates:
sublist([],[]).
sublist(X, [_ | RestY]) :-
sublist(X,RestY).
sublist([Item|RestX], [Item|RestY]) :-
sublist(RestX,RestY).
subtract([], _, []) :-
!.
subtract([Head|Tail],ToSubstractList,Result) :-
memberchk(Head,ToSubstractList),
!,
subtract(Tail, ToSubstractList, Result).
subtract([Head|Tail], ToSubstractList, [Head|ResultTail]) :-
!,
subtract(Tail,ToSubstractList,ResultTail).
generateAllPossibleSubsets(ListToSplit,sets(Sublist,SecondPart)) :-
sublist(Sublist,ListToSplit),
subtract(ListToSplit, Sublist, SecondPart).
These can then be used as follows:
:- findall(Set, generateAllPossibleSubsets(ObjectList,Set), ListOfSets ),
findMinimalDifference(ListOfSets,Set).
So because I think this is a wrong way to do it, I figured I'd try it in an iterative way. This is what I have so far:
totalWeightOfSet([],0).
totalWeightOfSet([Head|RestOfSet],Weight) :-
objectWeight(Head,HeadWeight),
totalWeightOfSet(RestOfSet, RestWeight),
Weight is HeadWeight + RestWeight.
findBestBalancedSet(ListOfObjects,Sets) :-
generateAllPossibleSubsets(ListOfObjects,sets(A,B)),
totalWeightOfSet(A,WeightA),
totalWeightOfSet(B,WeightB),
Temp is WeightA - WeightB,
abs(Temp, Difference),
betterSets(ListOfObjects, Difference, Sets).
betterSets(ListOfObjects,OriginalDifference,sets(A,B)) :-
generateAllPossibleSubsets(ListOfObjects,sets(A,B)),
totalWeightOfSet(A,WeightA),
totalWeightOfSet(B,WeightB),
Temp is WeightA - WeightB,
abs(Temp, Difference),
OriginalDifference > Difference,
!,
betterSets(ListOfObjects, Difference, sets(A, B)).
betterSets(_,Difference,sets(A,B)) :-
write_ln(Difference).
The issue here is that it returns a better result, but it hasn't traversed the entire solution tree. I have a feeling this is a default Prolog scheme I'm missing here.
So basically I want it to tell me "these two sets have the minimal difference".
Edit:
What are the pros and cons of using manual list iteration vs recursion through fail
This is a possible solution (the recursion through fail) except that it can not fail, since that won't return the best set.
I would generate the 30 objects list, sort it descending on weight, then pop objects off the sorted list one by one and put each into one or the other of the two sets, so that I get the minimal difference between the two sets on each step. Each time we add an element to a set, just add together their weights, to keep track of the set's weight. Start with two empty sets, each with a total weight of 0.
It won't be the best partition probably, but might come close to it.
A very straightforward implementation:
pair(A,B,A-B).
near_balanced_partition(L,S1,S2):-
maplist(weight,L,W), %// user-supplied predicate weight(+E,?W).
maplist(pair,W,L,WL),
keysort(WL,SL),
reverse(SL,SLR),
partition(SLR,0,[],0,[],S1,S2).
partition([],_,A,_,B,A,B).
partition([N-E|R],N1,L1,N2,L2,S1,S2):-
( abs(N2-N1-N) < abs(N1-N2-N)
-> N3 is N1+N,
partition(R,N3,[E|L1],N2,L2,S1,S2)
; N3 is N2+N,
partition(R,N1,L1,N3,[E|L2],S1,S2)
).
If you insist on finding the precise answer, you will have to generate all the partitions of your list into two sets. Then while generating, you'd keep the current best.
The most important thing left is to find the way to generate them iteratively.
A given object is either included in the first subset, or the second (you don't mention whether they're all different; let's assume they are). We thus have a 30-bit number that represents the partition. This allows us to enumerate them independently, so our state is minimal. For 30 objects there will be 2^30 ~= 10^9 generated partitions.
exact_partition(L,S1,S2):-
maplist(weight,L,W), %// user-supplied predicate weight(+E,?W).
maplist(pair,W,L,WL),
keysort(WL,SL), %// not necessary here except for the aesthetics
length(L,Len), length(Num,Len), maplist(=(0),Num),
.....
You will have to implement the binary arithmetics to add 1 to Num on each step, and generate the two subsets from SL according to the new Num, possibly in one fused operation. For each freshly generated subset, it's easy to calculate its weight (this calculation too can be fused into the same generating operation):
maplist(pair,Ws,_,Subset1),
sumlist(Ws,Weight1),
.....
This binary number, Num, is all that represents our current position in the search space, together with the unchanging list SL. Thus the search will be iterative, i.e. running in constant space.
I am studying prolog at university and facing some problems. What I already found out is just solution to a problem. However, I'm more interested in the way to think, i.e. how to get such solution.
Can somebody give me an advise on this field. I would really appreciate your help.
I give an example I am coping with and also, found a solution on stackoverflow here, but what I looking for is how does he do that, how does he find the answer :)
Write a predicate flatten(List,Flat) that flatten a list, e.g. flatten([a,b,[c,d],[[1,2]],foo],X) will give X=[a,b,c,d,1,2,foo].
This is the answer I found on stackoverflow:
flatten(List, Flattened):-
flatten(List, [], Flattened).
flatten([], Flattened, Flattened).
flatten([Item|Tail], L, Flattened):-
flatten(Item, L1, Flattened),
flatten(Tail, L, L1).
flatten(Item, Flattened, [Item|Flattened]):-
\+ is_list(Item).
this answer belongs to user gusbro and asked by user Parhs, I have try to find a way to contact user gusbro to ask him how he can derive such answer but I cannot.
Thank you very much.
Well, all I can say is that the way to solve a problem depends largely on the problem itself. There is a set of problems which are amenable to solve using recursion, where Prolog is well suited to solve them.
In this kind of problems, one can derive a solution to a larger problem by dividing it in two or more case classes.
In one class we have the "base cases", where we provide a solution to the problem when the input cannot be further divided into smaller cases.
The other class is the "recursive cases", where we split the input into parts, solve them separately, and then "join" the results to give a solution to this larger input.
In the example for flatten/2 we want to take as input a list of items where each item may also be a list, and the result shall be a list containing all the items from the input. Therefore we split the problem in its cases.
We will use an auxiliary argument to hold the intermediate flattened list, and thats the reason why we implement flatten/3.
Our flatten/2 predicate will therefore just call flatten/3 using an empty list as a starting intermediate flattened list:
flatten(List, Flattened):-
flatten(List, [], Flattened).
Now for the flatten/3 predicate, we have two base cases. The first one deals with an empty list. Note that we cannot further divide the problem when the input is an empty list. In this case we just take the intermediate flattened list as our result.
flatten([], Flattened, Flattened).
We now take the recursive step. This involves taking the input list and dividing the problem in two steps. The first step is to flatten the first item of this input list. The second step will be to recursively flatten the rest of it:
flatten([Item|Tail], L, Flattened):-
flatten(Item, L1, Flattened),
flatten(Tail, L, L1).
Ok, so the call to flatten(Item, L1, Flattened) flattens the first item but passes as intermediate list an unbound variable L1. This is just a trickery so that at the return of the predicate, the variable L1 still remain unbounded and Flattened will be of the form [...|L1] where ... are the flattened items of Item.
The next step, which calls flatten(Tail, L, L1) flattens the rest of the input list and the result is bounded with L1.
Our last clause is really another base case, the one that deals with single items (which are not lists). Therefore we have:
flatten(Item, Flattened, [Item|Flattened]):-
\+ is_list(Item).
which checks whether item is a list and when it is not a list it binds the result as a list with head=Item and as tail the intermediate flattened list.
First, I'll show you my approach to the problem, then I've got some resources for learning to think recursively.
Here's my solution to the problem "flatten a list of lists (of lists ...)". I've annotated it to show how I got there:
First, let's define the public interface to our solution. We define flatten/2. It's body consists of a call to the internal implementation flatten/3, which takes an accumulator, seeded as an empty list.
flatten ( X , R ) :-
flatten ( X , [] , R ) ,
.
That was easy.
The internal predicate flatten/3 is a little more complex, but not very.
First, we have the boundary condition: the empty list. That marks the end of what we need to do, so we unify the accumulator with the result:
flatten( [] , X , X ).
The next (and only) other case is a non-empty list. For this, we examine the head of the list. Our rule here is that it needs to flattened and appended to the result. A good rule of programming is to write descriptive code, and Prolog is itself a descriptive, rather than procedural, language: one describes the solution to the problem and lets the inference engine sort things out.
So...let's describe what needs to happen now, and punt on the mechanics of flattening the head of the list:
flatten( [X|Xs] , T , Y ) :-
flatten_head(X,X1) ,
append( T,X1,T1) ,
flatten( Xs , T1 , Y )
.
That, too, was easy.
That's the essence of the entire solution, right there. We've broken our problem into 3 pieces:
a special case (the empty list)
the normal case (a non-empty list)
what to do with each element in the list (not yet defined).
Let's move on to the implementation of how to flatten a single list element. That's easy, too. We've got two cases, here: the list item might be a list, or it might be something else.
First, the list element might be an unbound variable. We don't want untowards behaviour, like unbounded recursion happening, so let's take care of that straightaway, by disallowing unbound terms (for now). If the element is bound, we try to flatten it by invoking our public interface, flatten\2 again (oooooooh...more recursion!)
This accomplishes two things
First, it tells us whether we've got a list or not: flatten/2 fails if handed something other than a list.
Second, when it succeeds, the job of flatten_head/2 is done.
Here's the code:
flatten-head( X , Y ) :-
nonvar(X) ,
flatten( X , Y )
.
Finally, the last case we have to consider is the case of list elements that aren't lists (unbound vars, atoms or some other prolog term). These are already "flat"...all we need to do is wrap them as a single element list so that the caller (flatten\3) gets consistent semantics for its "return value":
flatten-head( X , [X] ).
Here's the complete code:
flatten ( X , R ) :-
flatten ( X , [] , R )
.
flatten( [] , X , X ) .
flatten( [X|Xs] , T , Y ) :-
flatten_head(X,X1) ,
append( T,X1,T1) ,
flatten( Xs , T1 , Y )
.
flatten-head( X , Y ) :-
nonvar(X) ,
flatten( X , Y )
.
flatten-head( X , [X] ) .
Each individual step is simple. It's identifying the pieces and weaving them together that's difficult (though sometimes, figuring out how to stop the recursion can be less than obvious).
Some Learning Resources
To understand recursion, you must first understand recursion—anonymous
Eric Roberts' Thinking Recursively (1986) is probably the best (only?) book specifically on developing a recursive point-of-view WRT developing software. There is an updated version Thinking Recursively With Java, 20th Anniversary Edition (2006), though I've not seen it.
Both books, of course, are available from the Usual Places: Powell's, Amazon, etc.
http://www.amazon.com/Thinking-Recursively-Eric-S-Roberts/dp/0471816523
http://www.amazon.com/Thinking-Recursively-Java-Eric-Roberts/dp/0471701467
http://www.powells.com/biblio/61-9780471816522-2
http://www.powells.com/biblio/72-9780471701460-0
You might also want to read Douglas Hofstadtler's classic Gödel, Escher, Bach: An Eternal Golden Braid Some consider it to be the best book ever written. YMMV.
Also available from the Usual Suspects:
http://www.powells.com/biblio/62-9780140289206-1
http://www.amazon.com/Godel-Escher-Bach-Eternal-Golden/dp/0465026567
A new book, though not directly about recursive theory, that might be useful, though I've not seen it (it's gotten good reviews) is Michael Corballis' The Recursive Mind:
The Origins of Human Language, Thought, and Civilization