Program description
Aim of the program
My program is meant to calculate locations of shapes in a 20X15 sized plane. I have a list of shapes carrying the shape type, its id, its radius or height, and its prospective [X,Y] location on the plane. I have a different list of binary operations carrying only the shape type, its id, and its location relationship with another shape. With this information in the operations list, I should compute the [X,Y] locations of the shapes: Below is a description of the two lists:
List of shapes
I have a list of shapes: each shape is a list of the form:
[[shape, id],height/radius, [X,Y]]
A list of such shapes would look something like the below when it is printed out by Prolog:
[[[diamond,1],4,[_7948,_7954]],[[circle,3],6,[_7894,_7900]],[[square,1],4,[_7840,_7846]],[[circle,1],5,[_7786,_7792]]|_7800]
List of operations
A list of operations that should be carried out on the shapes each operation is of the form:
[[[circle,1],below,[square,1]]]
This means that circle 1 should appear below square 1 on the X,Y plane
Such a list when printed out by prolog would look something like the following:
[[[circle,1],below,[square,1]]|_8016]
The program
So I have computeShapeLocations/2. Its first argument is a list of operations and the second list is a list of shapes. It recursively goes over the list of operations getting the shape ids on both sides of the operation. eg circle 1 - below - sqaure 1 and sends the two shapes to the correct function to calculate the locations using CLPFD. For two shapes with a relative positioning of 'below' I use computeShapesBelow/2 which takes two shapes each of the form [[shape, id],height/radius, [X,Y]].
Steps in ComputeShapeLocations/2:
1. Get an operation of the form [[[circle,1],below,[square,1]]] from the list of operations
2. Fetch first id (circle 1), then type of relationship (below) then second id (square 1).
3. Fetch the shapes from the shapes list (ShapesOut)
4. Send the shapes to computeShapesBelow/2. This just uses clpfd to compare radius or height and the dimensions of my X,Y plane.
:- use_module(library(clpfd)).
computeShapeLocations([],_ShapesOut).
computeShapeLocations([Operation|Rest],ShapesOut) :- writeln(ShapesOut),
writeln([Operation|Rest]),
nth0(0,Operation,Subject1),
nth0(1,Operation,below),
nth0(2,Operation,Subject2),
Shape1 = [Subject1,H,Loc],
Shape2 = [Subject2,H2,Loc2],
member(Shape1,ShapesOut),
member(Shape2,ShapesOut),
writeln(Shape1),
writeln(Shape2),
writeln(Subject1),
writeln(Subject2),
computeShapeBelow(Shape1,Shape2),
computeShapeLocations(Rest,ShapesOut).
computeShapeBelow(Shape1,Shape2) :- nth0(2,Shape1,Location1),
nth0(2,Shape2,Location2),
writeln(Shape1),
writeln(Shape2),
nth0(1,Shape1,Dim1),
nth0(1,Shape2,Dim2),
nth0(0,Location1,Xcord1),
nth0(0,Location2,Xcord2),
nth0(1,Location1,Ycord1),
nth0(1,Location2,Ycord2),
Ycord1 #> Dim1, Ycord1 #< 15-Dim1,
Xcord1 #> Dim1, Xcord1 #< 20-Dim1,
Ycord2 #> Dim2, Ycord2 #< 15-Dim2,
Xcord2 #> Dim2, Xcord2 #< 20-Dim2,
Ycord2 #> Ycord1+Dim2+Dim1.
The problem:
In computeShapeLocations/2 my lookup is just bizarre( see step three above in steps of computeShapeLocations/2). I use member(ShapeId, ListOFshapesList) to fetch shapes from listofshapes given their ids [shape,id]. I then print out the results( writeln(Shape1), writeln(Shape2))and the image below shows just how the behavior is wrong. For the first shape (circle,1), the result is good and computeShapesBelow/2 even comes up with a proper limit of its X,Y location (6..14 and 6..9). For the second shape (Shape2 or square 1). It does not behave as expected and the clpfd limits result in lower infinities.
The reason is because this second search of [square,1] ignores an entry of [[square, 1], 4, [_2166, _2172]] which is in the list and instead somehow adds an extra [[square, 1], _2250, [_2262|...]] which it then uses to mess up my results.
In my opinion, the source of your problem is being obscured by two simple problems. I don't have all your code and I don't really know what you're trying to do, so I'll just talk about what I see and how I would proceed.
The first problem is that you are not making effective use of unification. For instance, you can replace this:
nth0(0,Operation,Subject1),
nth0(1,Operation,below),
nth0(2,Operation,Subject2),
With this:
[Subject1,below,Subject2] = Operation,
But, moreover, you don't really need Operation on its own, so you can move that into the head of your clause:
computeShapeLocations([[Subject1,below,Subject2]|Rest],ShapesOut) :-
As you start to make these changes your code will contract quite a bit and it should become a lot easier to see what is going on. What would make it even easier to understand would be using less listy representations. For instance, it's a little easier for me to understand what is going on in this command list:
[below(circle(1), square(1)), below(circle(2), square(1)), ...]
or even this, which you can do by adding an :- op declaration:
[circle(1) below square(1), circle(2) below square(1), ...]
and then your pattern matches will look even simpler, like:
compute(Shape1 below Shape2) :- ...
Similarly, for your shapes, it would be a little easier to understand what is going on if you have a little more structure:
shape(circle(1), 4, X#Y)
is a little more obvious to me than
[[circle,1], 4, [X,Y]]
I find it a little odd that you've got unbound variables inside your input list. I gather you're hoping they'll obtain values later on. I suppose there's nothing wrong with this approach, I'm just surprised to see a mixture of ground and nonground acting as inputs.
Your second source of trouble is that you're mixing several kinds of procedure together. I'm pretty sure you have a DCG parsing step going on somewhere. By parsing into this weak, listy representation in there, you're forcing yourself to do more work inside these methods destructuring your lists and obtaining their meaning. Consider:
command([Shape1,below,Shape2]) --> shape(Shape1), "below", shape(Shape2).
versus
command(Shape1 below Shape2) --> shape(Shape1), "below", shape(2).
Or,
shape_type(circle) --> "circle". shape_type(square) --> "square".
shape_name(shape(Name, Size, X#Y)) -->
shape_type(T), integer(ID),
integer(Size),
integer(X), integer(Y),
{ Name =.. [T, ID] }.
versus whatever you have now.
IOW, you could create structure during the parse that will simplify your life during the processing. Similarly, doing a lot of what looks to me like debug I/O is making your processing more complex.
find_shape(ShapeId, Shapes, Shape) :-
Shape = shape(ShapeId, _, _),
member(Shape, Shapes).
computeShapeLocations([], _).
computeShapeLocations([ShapeId1 below ShapeId2|Rest], Shapes) :-
find_shape(ShapeId1, Shapes, Shape1),
find_shape(ShapeId2, Shapes, Shape2),
computeShapeBelow(Shape1, Shape2),
computeShapeLocations(Rest, Shapes).
computeShapeBelow(shape(_, D1, X1#Y1), shape(_, D2, X2#Y2)) :-
Y1 #> D1, Y1 #< 15 - D1,
X1 #> D1, X1 #< 20 - D1,
Y2 #> D2, Y2 #< 15 - D2,
X2 #> D2, X2 #< 20 - D2,
Y2 #> Y1 + D2 + D1.
I think if I were staring at this I would find it a bit easier to debug.
Related
I have a family tree program in Prolog that contain next facts:
male(alex).
male(david).
male(peter).
etc
...
female(sofia).
etc
....
parent(alex, peter).
parent(sofia, peter).
etc
....
and some rules such as:
father(X, Y) :- parent(X, Y), male(X).
mother(X, Y) :- parent(X, Y), female(X).
etc
....
I need to find all fathers who has two or more children.
I think that I need to go through all fathers im my program, put their children to list and count it's length, then if >=2 I add this father to another list or just print him and go further.
Although in the general case when you want to find all fathers with at least N children your approach would be required, a much simpler approach is available when you are dealing with only two children: find a child twice, and see if the two children are different.
In Prolog this rule would look like this:
father_of_two(X) :-
father(X, A),
father(X, B),
A \= B.
All you need to do now is collecting all people who match father_of_two predicate into a list.
I have a predicate, which is true, if passed such list of pairs, for instance:
translatable([(dog,perro)], [(perro,hund)], [(dog,hund)])
Means - if "dog" translates to "perro", and "perro" translates to "hund", then it is true that "dog" translates to "hund".
Here follows full code. Returns/suggests first member of pair - given ((a, b), a) returns true, given ((a, b), X) returns X = a:
first((First, _), First).
Similar to "first", but for second pair member:
second((_, Second), Second).
This returns true if translatable word exists in list of tuples, and saves translation to Translation: (dog, Translation, [(bed,cama),(dog,perro)]
translation_exists(Word, Translation, [H|T]) :-
first(H, Word), second(H, Translation), !;
translation_exists(Word, Translation, T).
And resulting:
translatable(EnglishSpanish, SpanishGerman, EnglishGerman) :-
forall(member(Pair, EnglishGerman), (
first(Pair, Word),
second(Pair, ResultTranslation),
translation_exists(Word, Translation, EnglishSpanish),
translation_exists(Translation, ResultTranslation, SpanishGerman)
)).
This code returns true/false correctly.
But why, given
translatable([(dog,perro)], [(perro,hund)], X).
It does not returns X = [(dog,hund)]?
EDIT
To be more specific, actual goal is:
to find out if LAST dictionary has translatable pairs (and them only).
Daniel, thanks a lot, I have adopted your suggested member function - great simplification, thank you! This is all the code I have now:
lastIsTranslatable(_, _, []).
lastIsTranslatable(EngSpan, SpanGerm, [(Eng, Germ) | T]) :-
member((Eng, Span), EngSpan),
member((Span, Germ), SpanGerm),
% this is to protect endless [(dog,hund), (dog, hund), ...]
not(member((Eng, Germ), T)),
lastIsTranslatable(EngSpan, SpanGerm, T),
!.
And still, this works great finding True & False:
lastIsTranslatable([(a,b)], [(b,c)], [(a,c)]).
lastIsTranslatable([(a,b)], [(b,c)], [(a,no)]).
But for
lastIsTranslatable([(a,b)], [(b,c)], X).
result is X= [], then, after hitting ";" - false. Why?
Well, running with trace option, I see execution is failing on
not(member((Eng, Germ), T))
But otherwise resulting X will be endlessly filled with (a,c), (a,c)... Maybe there is better way to protect from duplicates?
The reason, basically, is that because EnglishGerman is uninstantiated, member/2 is free to come up with possible lists for it:
?- member((perro,X), List).
member((perro,X), List).
List = [ (perro, X)|_G18493911] ;
List = [_G18493910, (perro, X)|_G18493914] ;
List = [_G18493910, _G18493913, (perro, X)|_G18493917] ;
List = [_G18493910, _G18493913, _G18493916, (perro, X)|_G18493920]
...
This is the most direct issue, but even if you change the flow of data I think you'll still have problems:
translatable1(EnglishSpanish, SpanishGerman, EnglishGerman) :-
member((English,Spanish), EnglishSpanish),
member((Spanish,German), SpanishGerman),
member((English,German), EnglishGerman).
Note that I have foregone your first/2 and second/2 predicates in favor of pattern matching; I think this reads more clearly.
Aside: If you know your list is concrete and you don't want to generate multiple solutions, you can use memberchk/2 to verify that an element exists instead of member/2; it's cheaper and deterministic.
This works better (you get solutions, anyway) but still you get a lot more solutions than you need:
?- translatable1([(dog,perro)], [(perro,hund)], X).
X = [ (dog, hund)|_G18493925] ;
X = [_G18493924, (dog, hund)|_G18493928] ;
X = [_G18493924, _G18493927, (dog, hund)|_G18493931] a
Something which we know that our code does not know is that the cardinality of the result set should be less than or equal to the lowest cardinality of our inputs; if I have fifteen English-Spanish words and twelve Spanish-German words, I can't have more than twelve words in my English-German result. The reason our code doesn't know that is because it is trying to behave like math: our code is basically saying "for every element of English-Spanish, if there exists a matching element of Spanish-German, that is also an element of English-German." This does not tell us how to construct English-German! It only tells us a fact about English-German that we can verify with English-Spanish and Spanish-German! So it's cool, but it isn't quite enough to compute English-German.
Aside: it's conventional in Prolog to use a-b instead of (a,b); it's too easy to lull yourself into believing that Prolog has tuples when it doesn't and the operator precedence can get confusing.
So, how do we tell Prolog how to compute English-German? There are probably lots of ways but I would prefer to use select/3 because our set cardinality constraints (as well as a general sense that it will converge/halt) will emerge naturally from a computation that "uses up" the input sets as it goes.
translatable2([], _, []).
translatable2(_, [], []).
translatable2([Eng-Span|EngSpanRem], SpanGerm, EngGerm) :-
(select(Span-Germ, SpanGerm, SpanGermRem) ->
translatable2(EngSpanRem, SpanGermRem, EngGermRem),
EngGerm = [Eng-Germ|EngGermRem]
;
translatable2(EngSpanRem, SpanGerm, EngGerm)
).
The base cases should be obvious; if we are out of English-Spanish or Spanish-German, there's nothing left to compute. Then the inductive case peels the first item off the English-Spanish list and searches for a Spanish-German translation that matches. If it finds one, it uses it to build the result; otherwise, it just recurs on the remaining English-Spanish list. This way, on each iteration we at least discard an English-Spanish translation from that list, and we discard Spanish-German translations as they are used. So it seems intuitively likely that this will work and terminate without producing a bunch of extra choice points.
It seems to do the trick:
?- translatable2([dog-perro], [perro-hund], X).
X = [dog-hund] ;
X = [dog-hund].
The extra result there is because we hit both terminal cases because both lists became []; this isn't attractive but it isn't anything to worry about really either.
Now one thing that sucks about this solution is that it treats the first two parameters as in-parameters and the last one as an out-parameter and there isn't really anything you can do about this. I don't know if this is an issue for you; translatable/1 should not have this limitation, but because member((Spanish,German), SpanishGerman) happens before member((English,German), EnglishGerman) it winds up generating an infinitely large list, searching in effect for the missing Spanish-German translation.
Still, it feels like it should be possible to come up with a general purpose predicate that works as long as you supply any two of these inputs. I can do that if I know that all three lists are complete and in the same order:
translatable3([], [], []).
translatable3([X-Y|XYs], [Y-Z|YZs], [X-Z|XZs]) :-
translatable3(XYs, YZs, XZs).
And you can see it work like so:
?- translatable3([dog-perro], [perro-hund], X).
X = [dog-hund].
?- translatable3([dog-perro], X, [dog-hund]).
X = [perro-hund].
?- translatable3(X, [perro-hund], [dog-hund]).
X = [dog-perro].
But I don't know enough about your constraints to know if that could be a legitimate answer. My suspicion is no, because languages don't work that way, but who knows?
Anyway, that's three different approaches; I hope one of them is helpful to you!
I want to clear a list without cutting. I tried:
filter([],[]).
filter([H|T],[H|S]) :-
H<0,
filter(T,S).
filter([H|T],S) :-
H>=0,
filter(T,S).
But it doesn't work.
Here is what happened when I tried:
?- filter([1,0,-6,7,-1],L).
L = [-6,-1]; %false
no
L=[0,-6,-1] %true
Here's one way to do it:
filter([ ],[ ]).
filter([H|T],X) :-
( H > 0 -> X = Y ; X = [H|Y] ),
filter(T,Y).
Because the if-then-else construct in Prolog is sometimes described as having a "hidden cut", meaning that Prolog will not retry (backtrack) the logical outcome in the "if" portion of this construct (it commits to the first and only outcome), it's conceivable that your course instructor might object to this solution (even though no actual cut is used).
But your solution is partially wrong. You lump the zero elements with the positive ones, where your Question's wording suggests only the positive entries need to be "cleared" from the list.
I need to write a Prolog predicate mergealt(X,Y,Z) that succeeds if the list Z is a merger of alternate elements from the lists X and Y.
The input and output will like below:
?- mergealt([1,2,3,4],[6,7,8],Z).
Z = [1, 7, 3] .
?- mergealt([1,2,3,4],[6,7,8,9],Z).
Z = [1, 7, 3, 9] .
?- mergealt([1,2,3,4],[6,7,8,9,10],Z).
Z = [1, 7, 3, 9] .
I don't really understand recursion. How can I get started on this problem?
Prolog can be considered the 'flagman' of declarative languages.
So try to describe your problem, top down:
mergealt(X, Y, Z) :-
'take first element from X and put it in Z',
'discard first element from Y',
'mergealt rest-of-X and rest-of-Y, but exchange them'.
First step can't be accomplished if there are no elements in X.
This fact highlights the recursion termination case. Originally, Prolog didn't used if then else, instead alternatives are stated as different rules:
mergealt([], _Y, []).
Here you can see that pattern matching on first argument it's the key to distinguish alternatives, and contextually, Z get bound to an empty list. Y is unused, so it's marked as anonymus place holder, just to avoid a warning.
Then this simpler case suggests that we should use pattern matching to accomplish those verbose descriptions. See if you can complete the procedure with these guidelines:
mergealt([X|Xs], Y, [X|Zs]) :-
% take first element from X and put it in Z : done in the head
% discard first element from Y : see below
% mergealt rest-of-X and rest-of-Y, but exchange them'. : make your recursive call
discard_first_element([_|Rest], Rest).
% why is this necessary? do you see where it fails if we don't specify this case?
discard_first_element([], []).
Notice that the result always starts with the first element of the first list.
This means that, if the first list is empty, you know the answer right away.
Also notice that, if it isn't empty, we already know the first item of the result, so we need to use mergealt to compute the rest. But "the rest" will have the second item of the second list as the first item of the result, and as we said above, that means that a call to mergealt to compute it would have to have that be the first item of the first list (yeah, this is the tricky part).
I'm new to prolog and I just can't figure this out.
I'm trying to build a simple program that receives a list of predicates, searches for a specific predicate in the list, and applies a function to that predicate's parameters.
Something along these lines:
?- program([pred1(a,b,p), pred2(d,b,p), pred2 (a,c,p)]).
program (list1) :-
search(pred2(X,Y,p),list1).
doSomething (X,Y) % with the X and Y returned from search function, both of them.
Basically, I want to use all values that would return from an objective search(pred2(X,Y,p),list1) and use them on another function.
Okay, I tried some stuff in prolog and came to this:
member(X, [X | _]).
member(X, [_ | R]) :- member(X, R).
prog(L1,Out) :- member(pred2(X,Y), L1).
?- prog ([(pred1(a,b),pred2(c,b),pred2(d,a)],Out).
It gives true 2 times as it is supposed to, but I wanted to get Out = [c,b] and Out = [d,a]. How I can achieve this?
Regarding Oak's answer: I get that it isn't a procedural language but I can't figure out how to access values and use them in prolog. Your example wasn't that helpful.
For starters, I would avoiding calling these things "functions". Prolog is not a procedural language, and rules / predicates are not functions.
Basically, when you use a rule you're really asking Prolog, "give me all the values which will satisfy this rule". The rule, by itself, does not return anything.
So say you had the following in a procedural language:
f(g(3))
How would you do it in Prolog? You would need to write some predicate f(X,Y) and some predicate g(X,Y), and then you would need to use the query f(3,Y), g(Y,Z) - which means to ask Prolog to find you values for Y and Z which will satisfy this. Z is what you're interested in.
the best way to approach these filter & project requirements in prolog in my opinion is to write your filter expression such that it takes one argument and succeeds if the input argument passes the filter -
iseven(Num) :- 0 is Num % 2 .
Then write the the projection code as taking one argument that is the input, and one that is the output -
triple(NumIn, NumOut) :- NumOut is NumIn * 3 .
Then tie them together -
triple_evens(NumIn, NumOut) :- iseven(NumIn), triple(NumIn, NumOut).
Then to run this on every member of a list, we should use findall -
triple_evens_in_list(Lin, Lout) :-
findall(Num, ( member(NumL, Lin),
triple_evens(NumL, Num)
), LOut).
This could be generalized to take as arguments the name of the filter & map predicates of course. And it could be compressed down to one stmt too in the form -
findall(Num, ( member(M, List), 0 is M % 2, Num is M * 3 ), ListOut).