Prolog: put recursive predicates into Variable - list

I'm beginner in prolog and I need to resolve the following exercise:
Write a 3-predicate prereqchain(P,Q,L) that, given two courses P and Q, returns in L a representation of all the steps on a prerequisite path from P to Q.
For example prereqchain(comp085,itec450,L) should give the answer
L=prerequisite(comp085, comp101,
prerequisite(comp101, comp281,
prerequisite(comp281, itec450))).
Basically, the knowledge data base stand for the prerequisite relationship between the degree's courses. My main problem is, How can I capture the recursive calls trace and put it into variable ? On the other hand, is not really all the trace, because I just need the calls where the variables was replaced with the values.
Thanks a lot!

What's your base case? It's going to look like this:
prereqchain(Start, End, prerequisite(Start, End)) :-
prerequisite(Start, End).
This gives you the following situation:
?- prereqchain(comp281, itec450, L).
L = prerequisite(comp281, itec450).
So this is the first thing you need, now what happens when you ask for prereqchain(comp101, itec450)? You need an inductive case that follows the rest of the chain:
prereqchain(Start, End, prerequisite(Start, Middle, Tail)) :-
prerequisite(Start, Middle),
prereqchain(Middle, End, Tail).

Related

SWI-prolog : how to make a circular list by using some facts

loop1(a).
loop1(b).
loop1(c).
loop1(d).
circular(d, a).
circular(b, c).
circular(b, d).
circular(a, b).
so when I call:
in_cycle(a, Cycle).
it will return:
[a,b,d,a]
if I call:
in_cycle(b, Cycle).
it will return:
[b,d,a,b].
however, if you call:
in_cycle(c, Cycle).
it will return:
false. (because no loop is included).
here is my try:
in_cycle(C,Cycle) :- circular(C,Cycle1),in_cycle(Cycle1,Cycle).
I know this predicate has very serious problem : it won't stop...I really want to know what kind of base case I should add so this predicate will stop ? Should i add a condition so prolog will stop when it find the same alphbet ?
It would be grateful if someone could help me!
-------updated-------
check([Y,_|Z]) :- check([Y|Z]).
in_cycle(C, [C]).
in_cycle(C, [C, C1|Cycle]) :- circular(C, C1),check([C,C1|Cycle]),
in_cycle(C1, [C1|Cycle]).
What is the shortest cycle you could have in your fact database? Would circular(a, a). be cycle [a]? Knowing what the shortest cycle is might help you find (one of) the finishing condition(s) for your predicate.
To be able to find a list, your predicate needs to build it. Your in_cycle/2 never mentions any lists. You need to use the [Head | Tail] construct somewhere in there to be able to add elements to a list.
You already know something about what your cyclical list looks like. The first and last element are the same, and they are the same as the symbol you're trying to find the cycle for. Use that information.
To be able to tell when you completed a cycle, you need to remember which symbol you started with. To be able to do that with recursion, you need to keep state. So you're going to need an additional predicate where you keep that state. E.g. something involving a predicate like in_cycle(Current_symbol, Cycle, Start_symbol). You can then call that from your in_cycle/2.
Let's have a look at your try:
in_cycle(C, Cycle) :-
circular(C, Cycle1),
in_cycle(Cycle1, Cycle).
You can use the trace command at the prompt to see what's happening: trace, in_cycle(a, X).
Press Space to step through your program. Press h for help, and a to exit. Use notrace. to get out of trace mode again.
As you step through this, you'll find that your predicate is nicely looping through the cycle, but at no point does the X ever become a list. That's bad.
Let's try and make this build a list. As I mentioned in point (3), you already know something about the list. The first element of the list is the same as the first argument to in_cycle. Even more, the second element of the list is the same as the element you'll find with circular/2. So we know a cycle has at least two elements. How about this?
in_cycle(First, [First, Next|Cycle]) :-
circular(First, Next),
in_cycle(Next, [Next|Cycle]).
If you trace this now, you'll see something is happening with X, but still not actually anything useful. Cycle remains a mystery and we're just looping through the facts forever. You need some end condition here. Let's try a simple one:
in_cycle(First, [First]).
in_cycle(First, [First, Next|Cycle]) :-
circular(First, Next),
in_cycle(Next, [Next|Cycle]).
Whoa! in_cycle(a, X) suddenly gives results! All possible lists using circular connections starting with a, it seems. That's not exactly what we want, but maybe we're getting closer?
One problem with this is that in_cycle(Next, [Next|Cycle]) is not actually correct!
If you do in_cycle(a, X), you already know that X should become [a, b, d, a], so filling those values into in_cycle(First, [First, Next|Cycle]), you get:
First = a
Next = b
Cycle = [d, a]
When you get to in_cycle(Next, [Next|Cycle]), that means it's in_cycle(b, [b, d, a]). But [b, d, a] is not a cycle! You need to be able to distinguish these two situations somehow. One way of doing that is to call a separate predicate like I mentioned in (4) to keep track of what your starting element was.
A node is in a cycle, if you can find a path back to that very node. Using path/4:
in_cycle(C, [C|Cs]) :-
circular(C, A),
path(circular, Cs, A,C).
Now, does this predicate terminate? How can we test this in a systematic manner? How can we ensure that we do not forget any special case? For pure, monotonic programs as this one, testing for termination is trivial1: Simply take the most general query! That is:
?- in_cycle(C, Cs).
C = d, Cs = "dabd" % [d,a,b,d]
; C = b, Cs = "bdab"
; C = a, Cs = "abda"
; false. % it terminates!
(See this answer how to get "bdab" in place of [b,d,a,b]).
What is so nice in Prolog is that above query constitutes a proof of termination. Each query you can pose is included in above query. And since the more general query already terminates, any more specific query will terminate too! Any!
And all of this holds even for any variable free facts for circular/2. However, this proof cannot be carried out so easily as the proof for a specific set of facts.
1 Note that trivial means belonging to the trivium.

Prolog Predicate Solution

I am going through some past exam questions for my prolog exam that is coming up.
Here is the question:
(a) Write a predicate insert(Xs, Y, Zs) that holds when Zs is the list obtained
by inserting Y into the list Xs. A query such as:
? - insert([1,2,3], 4, Zs).
should succeed four times and give the following answers:
Zs = [4, 1, 2, 3]
Zs = [1, 4, 2, 3]
Zs = [1, 2, 4, 3]
Zs = [1, 2, 3, 4].
I'm a bit concerned because I have no idea where to start. Would anyone be able to help out as I need example solutions to practice for my exam.
Would really appreciate any help with this.
We start by changing the terrible name of this predicate: The predicate should describe what holds, not what to do. The name should reflect that. I suggest list_with_element/3, and encourage you to try finding even better names, ideally making clear what each argument stands for.
Then, we do what we set out to do: Describe the cases that make this relation hold.
For example:
list_with_element([], E, [E]).
list_with_element([L|Ls], E, [E,L|Ls]).
list_with_element([L|Ls0], E, [L|Ls]) :-
...
I leave filling in the ... as an easy exercise. State the condition that is necessary for the clause head to be true!
EDIT: I would like to say a bit more about the pattern above. In my experience, a good way—and definitely in the beginning—to reason about predicates that describe lists is to consider two basic cases:
the atom [], denoting the empty list
terms of the form '.'(E, Es), also written as [E|Es], where E is the first element of the list and Es is again a list.
This follows the inductive definition of lists.
The drawback in this specific case is that this approach leads to a situation where case (2) again needs to be divided into two subcases, and somehow unexpectedly necessitates three clauses to handle the two basic cases. This obviously runs counter to our intuitive expectation that two clauses should suffice. Indeed they do, but we need to be careful not to accidentally lose solutions. In this case, the first two clauses above are both subsumed by the fact:
list_with_element(Ls, E, [E|Ls]).
Every experienced Prolog coder will write such predicates in this way, or just, as in this case, use select/3 directly. This is what #lurker sensed and hinted at, and #tas correctly shows that a different clause (which is easy to come up with accidentally) does not fully subsume all cases we want to express.
Thus, I still find it a lot easier to think first about the empty list explicitly, make sure to get that case correct, then continue with more complex cases, and then see if you can write the existing program more compactly. This is the way I also used for this sample code, but I did not make it as short as possible. Note that with monotonic code, it is completely OK to have redundant facts!
Note that is is specifically not OK to just replace the first two clauses by:
list_with_element([L|Ls], E, [E|Ls]).
because this clause does not subsume case (1) above.
I guess that one answer that the question might be looking for goes along these lines:
insert(List, Element, NewList) :-
append(Front, Back, List), % split list in two
append(Front, [Element|Back], NewList). % reassemble list
If you would like a declarative reading:
NewList has Element between the front and the back of List.
Check carefully if append/3 or a predicate with the same semantics appears in the earlier questions or the study material.
And note that this is in essence the exact same solution as the suggestion by #mat, if I understand it correctly. Consult the textbook definition of append/3 for details. Or even better, look at the textbook definition of append/3 and adapt it to use if for "inserting".
There is a built-in predicate select/3 that does the same thing, although with the arguments in a different order.
Remember that (if defined correctly) a predicate can work in different directions. For instance, it can tell you what a list would look like after removing an element, it can (although it's fairly trivial) tell you what element to remove from one list to get another, or it can tell you what lists, after having a given element removed, would resemble a given list.
(Hint: you may want to look into that last one).

Prolog Beginner: Reverse List only once

Assume I have two arbitrary lists that represent the first two items of a 3-place predicate:
[anna,berta,charlotte],[charles,bob,andy]
I want to match every item in a third list (the third item of the 3-place predicate) as follows:
[[anna,andy],[berta,bob],[charlotte,charles]]
Basically the items get matched in a sequentially reverse fashion. To match the items in a sequential manner, I've devised the following code:
match([],[],[]).
match([A|At],[C|Ct],[[A,C]|Dt]):-match(At,Ct,Dt).
But this would give me the following:
match([anna,berta,charlotte],[charles,bob,andy],X).
X=[[anna,charles],[berta,bob],[charlotte,andy]]
So I need to reverse the second list somehow. So far, I've altered the code as follows:
match([],[],[]).
match([A|At],[C|Ct],[[A,B]|Dt]):-reverse([C|Ct],[B|Bt]),match(At,Bt,Dt).
But this would continually reverse the second list with each pass. The result would look as follows:
match([anna,berta,charlotte],[charles,bob,andy],X).
X=[[anna,andy],[berta,charles],[charlotte,bob]]
Question:
How do I reverse the second list only ONCE, so the actual results match the desired ones? Or is my approach fundamentally flawed? I'm new to prolog and am currently stymied by this. Any help would be appreciated.
Do exactly what you say: Reverse the list once, and then use the reversed list.
lists_pairs(Ps1, Ps2, Pairs) :-
reverse(Ps2, RPs2),
pairs_keys_values(Pairs, Ps1, RPs2).
You can check out the source code of reverse/2 and pairs_keys_values/3 in any decent Prolog library to see how it is defined.
Sample query and answer:
?- lists_pairs([anna,berta,charlotte], [charles,bob,andy], Ps).
Ps = [anna-andy, berta-bob, charlotte-charles].
I leave converting such pairs to the non-sensible "pair as list" representation as an exercise.
The trick to solving problems that require you to apply a rule only once is to build an auxiliary rule which performs extra steps before and/or after invoking the recursive rule:
match(A, B, R) :- reverse(B, RevB), match_impl(A, RevB, R).
match_impl([], [], []).
match_impl([A|At], [C|Ct], [[A,C]|Dt]) :- match_impl(At, Ct, Dt).
match_impl/3 is your match/3 rule renamed to avoid conflicting with the "top" match/3 rule that includes an auxiliary step.
This is a small followup to #mat's answer.
To aid termination in some cases you could add a redundant same_length_as/3 goal like so:
lists_pairs(Ps1, Ps2, Pairs) :-
same_length_as(Ps1, Ps2, Pairs),
reverse(Ps2, RPs2),
pairs_keys_values(Pairs, Ps1, RPs2).
The auxiliary predicate same_length_as/3 can be defined like this:
same_length_as([],[],[]).
same_length_as([_|As],[_|Bs],[_|Cs]) :-
same_length_as(As,Bs,Cs).

Understanding the splitting in Swi-prolog

I have this code for splitting input list into its halves. It seems to be OK.
halve(List,A,B) :- halve(List,List,A,B), !.
halve(B,[],[],B).
halve(B,[_],[],B).
halve([H|T],[_,_|T2],[H|A],B) :-halve(T,T2,A,B).
Ok, so I tried to decode it. The beginning is clear:
"Halve took list and 2 logic variables" is this:
halve(List,A,B)
(1) Then continuous this part:
:- halve(List,List,A,B).
And this means, that I am creating new two lists (List, List) from the first one or what? What exacly represents ":-"? I guess the new lists = halves will be the A, and B, right?
(2) Second, please, I don't quite get these two lines:
halve(B,[],[],B).
halve(B,[_],[],B).
Maybe you could explain it on some examples, please?
(3) Well, I hope after your explanation of (1) and (2), I'll get the final part by myself...
halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B).
Thank you very, very much for helping me.
Ok, our first problem already has its solution. Long story short, it works like this:
halve([1,2,3,4,5],[1,2],[3,4,5]).
->true
If you notice it splits the list into its halves but if the list has an odd number of the elements, the second half is the bigger one.
Now what I want to obtain is to have the first one bigger.
So I'm thinking about this:
I'm going to reach this:
Halves_div([1,2,3],A,B).
A=[1,2],
B=[3].
Let's say my input is list: [1,2,3]. So I'll start with splitting list's head and tail: [H|T] and then I will merge the H with new empty list - my 1st Half (A).
After that I have A=[1], B=[] and Input=[2,3].
For merging I have:
merge([],List,List).
merge([H|T],List,[H|New]) :- merge(T,List,New).
And one more thing - I need to check whether the 1st half is already >= 2nd half, right?
So this is my idea and only thing I'd love you to help me is to write it in prolog. I'm kinda confused how to put it together.
Thanks!
It seems my idea of solution is too complicated and I found something better!
To start, a Prolog clause looks like that:
Head :- Body
You can read that as "Head if Body", or "Body implies Head".
Note that sometimes you just have
Head
That's because Head is always true. Instead of calling Head a clause, we rather call it a fact in this case.
So here, we have:
halve(List,A,B) :- halve(List,List,A,B).
That means that halve(List, A, B) is true if halve(List, List, A, B) is true. Concretely it's just a way to delegate the work of halve/3 to halve/4, a so called worker predicate.
Why do we need a worker predicate? Well, because here we'd like to use another variable to calculate our A and B terms. But we couldn't do that with halve/3 because the 3 argument spots of halve/3 were already taken by the input list, List, the first half of the result, A and the second half of the result, B.
About the List, List thing, it's just a way to say that we call halve/4 with the same first and second argument, like you would in any programming language.
Then the interesting stuff starts. Prolog will try to prove that halve/4 is true for some given arguments. Let's say to illustrate the execution that we called halve/3 this way:
?- halve([1, 2], A, B).
Then, if you followed what I talked about previously, Prolog will now try to prove that halve/3 is true by proving that halve/4 is true with the following arguments: halve([1, 2], [1, 2], A, B)..
To do that, Prolog has 3 choices. The first choice is the following clause:
halve(B,[],[],B).
Obviously, that won't work. Because when Prolog will try to fit the second argument of the caller "in" the second argument of the callee through unification, it will fail. Because
[1, 2] can't be unified with [].
Only two choices left, the next is:
halve(B,[_],[],B).
Same thing here, Prolog cannot unify [1, 2] and [_] because _ is just a variable (see my post about the anonymous variable _ if you've troubles with it).
So the only chance Prolog has to find a solution to the problem you presented it is the last clause, that is:
halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B).
Here, Prolog will find a way to unify thing, let's see which way:
we have to unify [1, 2] with [H|T]. That means that H = 1. and T = [2].
we have to unify [1, 2] with [_,_|T2]. that means that T2 = [].
now we start to build our results with the next unification, ie A = [H|A'] (I primed the second A because variables are scoped locally and they are not the same). Here we tell that when we'll have our result calculated from the body of the clause, we'll add H to it. Here H is 1 so we already know that the first element of A will be 1.
Ok ok, unification succeeded, great! We can proceed to the body of the clause. It just calls halve/4 in a recursive manner with those values (calculated above):
halve([2], [], A, B).
And here we start all over again. Though this time things will be fast since the first choice Prolog has will be a good fit:
halve(B,[],[],B).
can be unified to
halve([2], [], A, B).
with those values: A = [] and B = [2].
So that's a good step, we now reached the "base case" of the recursion. We just have to build our result from bottom to top now. Remember when we called recursively our predicate halve/4 a few steps above? We had already said that the first element of A would be 1. Now we know that the tail is [] so we can state that A = [1]. We hadn't stated anything particular about B so B = [2] is left untouched as the result.
Now that I detailed the execution, you might wonder, why does this work? Well, if you pay attention, you'll note that the second argument of halve/4 is gone through twice as fast as the first one. [H|T] vs [_, _|T2]. That means that when we hit the end of the list with our second argument, the first one is still at the middle of our list. This way we can divide the thing in two parts.
I hope I helped you catch some of the subtle things at work here.
halve(List,A,B) copies first half of List to A and unifies second half with B
That will be true when length of our list will be even: halve(B,[],[],B).
That will be true when length of out list will be odd: halve(B,[_],[],B).
halve([H|T],[_,_|T2],[H|A],B) :- halve(T,T2,A,B).
Here we are setting 2 lets call them 'pointers' in each step we copy one element from beginning of our list to A because we want get first half.
Because in each step we are removing 2 elements from our list [_,_|T2] Predicate will stop when list will have only one left element or empty, then it will unify rest of our list with B. If you cant understand use trace/0
This version might prove useful ...
split_in_half(Xs, Ys, Zs) :- length(Xs, Len),
Half is Len // 2, % // denotes integer division, rounding down
split_at(Xs, Half, Ys, Zs).
split_at(Xs, N, Ys, Zs) :- length(Ys, N), append(Ys, Zs, Xs).

Put the information from a leaf in a list

I want to write a program that puts the information from the leaves in a tree in a list. I tried doing this:
leaves(l(_), [_]).
leaves(b(B1, B2), [L]):- leaves(B1, [L1]), leaves(B2, [L2]). append(L1, L2, L).
But it gives me L=[_A]. Why is that?
There are several errors in your code, the correct code looks like this:
leaves(l(X), [X]).
leaves(b(B1, B2), L):- leaves(B1, L1), leaves(B2, L2), append(L1, L2, L).
I think the main problem was the use of _. It's an anonymous variable, it means “anything can be here”. And if you have it twice in one term, both _ are different variables.
Also, you have . before append instead of ,. My Prolog interpreter (SWI-Prolog) reported two warnings about singleton variables, you shouldn't ignore those.
Also, as you describe a list, consider using DCG notation:
leaves(l(L)) --> [L].
leaves(b(B1,B2)) --> leaves(B1), leaves(B2).
Usage: ?- phrase(leaves(Tree), Leaves).
debug your program with trace, that would execute your query step by step so you can see why the result is wrong. basically that variable given as list entries usually caused by singleton variables (declared but not used, i.e. instantiated) as svick has stated.