Related
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 am currently studying logic programming, and learn Prolog for that case.
We can have a Knowledge Base, which can lead us to some results, whereas Prolog will get in infinite loop, due to the way it expands the predicates.
Let assume we have the following logic program
p(X):- p(X).
p(X):- q(X).
q(X).
The query p(john) will get to an infinite loop because Prolog expands by default the first predicate that is unified. However, we can conclude that p(john) is true if we start expanding the second predicate.
So why doesn't Prolog expand all the matching predicates (implemented like threads/processes model with time slices), in order to conclude something if the KB can conclude something ?
In our case for example, two processes can be created, one expanded with p(X) and the other one with q(X). So when we later expand q(X), our program will conclude q(john).
Because Prolog's search algorithm for matching predicates is depth-first. So, in your example, once matching the first rule, it will match again the first rule, and will never explore the others.
This would not happen if the algorithm is breadth-first or iterative-deepening.
Usually is up to you to reorder the KB such that these situations never happen.
However, it is possible to encode breadth-first/iterative-deepening search in Prolog using a meta-interpreter that changes the search order. This is an extremely powerful technique that is not well known outside of the Prolog world. 'The Art of Prolog' describes this technique in detail.
You can find some examples of meta-interpreters here, here and here.
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.
When I sort [(101,a),(42,b),(85,b)] is Prolog with sort([(101,a),(42,b),(85,b)],X). is get X = [ (42, b), (85, b), (101, a)]. But how come? Does Prolog recognize the tuples and sort them on the first element and then on the second element?
You should really simply look at the exact documentation of the Prolog you are using. In SWI-Prolog, for example, sorting is on "standard order". For compound terms (as you are using), it is first arity, then name, then recursively arguments. So in your case, yes, it is sorted first on first and then on second argument.
By the way, ISO sort should remove duplicates, not that you get surprised by it.
And strictly speaking, there are no "tuples" in Prolog. What you have there is the functor , with arity 2 (or, ,/2). Look at this:
2 ?- write_canonical((42, b)).
','(42,b)
true.
Your assumption seems reasonable. We can check some documentation, personally I like the documentation for ciao.
See page 235, then page 115. Notice you could also sort by keys.
You should be aware that some people consider using this kind of predicates (non-declarative) a bad practice. Basically there are two terms in this predicate, one must be grounded and the other one must not, so in fact this is a function and not a logical predicate. Those worried for the "purity" of logic programming would probably find a workaround not to use that.
Lets say I've got the following predicates:
father(aaron, chloe).
father(aaron, dan).
father(aaron, emily).
father(frank, george).
mother(beth, chloe).
mother(beth, dan).
mother(beth, emily).
mother(emily, george).
sibling(X,Y) :-...
parents(X,Y) :-...
I want to find the shortest route (distance) between two members of the family tree.
The distance between parents for instance is 2, and between brothers (siblings) is one.
I've tried the following (but it didn't work):
parent(X,Y) :-
father(X,Y);
mother(X,Y).
sibling(X,Y):-
parent(Z,X), !, parent(Z,Y),
not(X=Y).
not_in_list(_,[]).
not_in_list(X,[Y|L]):-
not(X=Y),
not_in_list(X,L).
edge(X,Y):-
(parent(X,Y);
parent(Y,X);
sibling(X,Y)).
list_length(0,[]).
list_length(N,[_|Ys]):-
list_length(N1,Ys),
N is N1+1.
travel_graph(X,X,_).
travel_graph(From,To,[From|Path]):-
edge(From,Next),
not_in_list(Next,Path),
travel_graph(Next,To,Path).
degree(X,Y,N):-
travel_graph(X,Y,L),
list_length(N,L).
In general when you want shortest paths you want a breadth-first search. There's some discussion here: Breadth-First in Prolog
Prolog is going to try to find solutions depth first, so it's going to see just how far a particular line of reasoning can take it. When you want the shortest path, you would rather Prolog try all the first options, then iterate outward one from each of those, until you find a solution. This way, the first solution you find will be the shortest. It's the only way to go if you might have infinite solutions (such as walking a maze where you can back up and retrace your steps).
Unfortunately there's no magic switch you can flip to get breadth-first search, so you'll have to implement it. O'Keefe explains this very lucidly in The Craft of Prolog, by explaining first how to convert a normal Prolog search into an explicit depth-first search with an "open set" of things yet-to-be-tried, and then how to change the order of the list representing the open set to get breadth-first behavior instead.