Cuts in recursion of Prolog - prolog

I am learning Prolog myself and recently came across a piece of notes:-
Example: nth/3
for example, to find the nth element of a list we could use
nth([X|_],0,X).
nth([_|L],N,X) :- N1 is N - 1, nth(L,N1,X).
the 0th element of a list is the head of the list (first clause), otherwise
we take the n-1th element of the tail of the list (second clause).
once we have found the nth element, we can’t find any more by backtracking.
nth([X|_],0,X).
nth([_|L],N,X) :- N1 is N - 1, !, nth(L,N1,X).
adding a cut makes this clear and may be necessary for tail recursion
optimisation.
However, when I use the trace function in Prolog, I found out that the calling sequences of these 2 pieces of codes are exactly the same.
Should I put the ! mark as follows instead?
nth([X|_],0,X) :- !.
nth([_|L],N,X) :- N1 is N - 1, nth(L,N1,X).

The cut in the second clause is redundant as there are no more clauses and the goal before it (the is/2 call) is deterministic. I also don't think that the cut plays any role on tail recursion optimization (which basically requires that you recursive call be the last goal in the clause body).
However, before worrying about cut placement, you should check what happens if you call the nth/3 predicate in a mode other than nth(+,+,-). For example, what happens in mode nth(+,+,+), i.e. when you call the predicate with all arguments instantiated, but the element is not in the list? Hint: moving the cut to the first clause will not solve the problem.

Related

How to find the last item in the third list, which is collected from the first and second list?

intersection([],L1,L2,L3).
intersection([H|T],L2,L3,[H|L4]):-member(H,L2),intersection(T,L3,L3,L4).
member(H,[H|T]).
member(X,[H|T]):-member(X,T).
This code makes the third list from the first and second list.
last([U],U).
last([_|L3],U) :- last(L3,U).
This piece of code looks for the last item in the list.
My problem is that I can’t figure out how to make these two pieces of code fit into one. That is, the program should find duplicate elements in the first and second list and display them in the third, and from the third list, display the last element multiplied by 3.
The main problem is intersection/4. I assume you wanted to write a deterministic predicate intersection/3 the first two arguments of which are fully instantiated at call time and the last argument of which is an output argument. By deterministic, I mean that intersection/3 should succeed exactly once without leftover choice points. The SWI-Prolog documentation contains a useful overview of determinism and mode declarations (although it does not enforce them).
It is useful to begin by writing a declarative specification of the predicate following the inductive definition of lists:
The intersection of [] and Ys is [].
The intersection of [A|Xs] and Ys is A prepended to the intersection of Xs and Ys if A is a member of Ys.
The intersection of [A|Xs] and Ys is the intersection of Xs and Ys if A is not a member of Ys.
The simplest translation of this specification into standard Prolog is:
intersection([],_,[]).
intersection([A|Xs],Ys,[A|Zs]) :-
member(A,Ys),
intersection(Xs,Ys,Zs).
intersection([A|Xs],Ys,Zs) :-
\+ member(A,Ys),
intersection(Xs,Zs).
If the first call to member/2 succeeds the second should fail. In order to avoid backtracking, unifying the current goal with the head of the second clause, and performing a redundant call to member/2, we place a cut after the occurrence of member/2 in the second clause.
intersection([],_,[]).
intersection([A|Xs],Ys,[A|Zs]) :-
member(A,Ys),
!,
intersection(Xs,Ys,Zs).
intersection([_|Xs],Ys,Zs) :-
intersection(Xs,Ys).
If the current goal unifies with the head of the first clause, it will not unify with the heads of later clauses. In order to prevent spurious backtracking, we place a cut in the (empty) body of the first clause. Whether this cut is necessary depends on your choice of Prolog implementation.
intersection([],_,[]) :-
!.
intersection([A|Xs],Ys,[A|Zs]) :-
member(A,Ys),
!,
intersection(Xs,Ys,Zs).
intersection([_|Xs],Ys,Zs) :-
intersection(Xs,Ys).
We are only interested in checking membership in the second list. Thus, we can replace the occurrence of member/2 with the semideterministic predicate memberchk/2. Here, semideterministic means that memberchk/2 succeeds or fails exactly once without leftover choice points.
intersection([],_,[]).
!.
intersection([A|Xs],Ys,[A|Zs]) :-
memberchk(A,Ys),
!,
intersection(Xs,Ys,Zs).
intersection([_|Xs],Ys,Zs) :-
intersection(Xs,Ys).
This implementation of intersection/3 is nearly identical to the implementation found in the SWI-Prolog library lists.pl. You can combine this predicate with an implementation of last/2 in order to complete your program. For example:
last_duplicate_tripled(Xs,Ys,N) :-
intersection(Xs,Ys,Zs),
last(Zs,M),
N is M * 3.
From here, I recommend doing the following:
Implement intersection/3 using metalogical predicates like findall/3.
Read #false's answer to this question.
Read #repeat's answer to this question.
They are doing something much more interesting and sophisticated than I attempted in this answer.

Why does these rules work? (finding last element in list in prolog)

I am trying to understand the following set of rules, which supposed to find the last element in a list (for instance my_list(X,[1,2,3]) gives X=3).
my_last(X,[X]).
my_last(X, [_|L]) :- my_last(X, L).
I understand the first fact - X is the last element of a list if X is the only element in it, but how does the second rule work? this looks a bit strange to me as a prolog noob, I'd love to know if there's an intutive way to interprate it.
T
Read the Prolog predicate as logical rules. If there is more than one predicate clause for the predicate, you can think of it as "OR":
my_last(X,[X]).
X is the last element of the list, [X].
my_last(X, [_|L]) :- my_last(X, L).
X is the last element of the list [_|L] if X is the last element of the list L.
The second clause is a recursive definition. So it does, ultimately, need to terminate with the recursive call not matching the head of the recursive clause that is calling it. In this case, L eventually becomes [] and will no longer match [_|L]. This is important because, otherwise, you will get an infinite recursion.

How do recursive functions work in Prolog?

I was reading a recursive function in prolog that returns the Sum of all the elements in a list as below :
sum([ ], 0).
sum( [Elem | Tail], S):- sum(Tail, S1),
S is S1 + Elem.
I can't understand two issues:
1: In the left side of ":-" we have the Goal. It means all the calculations will be done in the right side of ":-" and then we can use the Goal as a normal function. It means we give our arguments and variables that the result will be putted on them, and the right side is responsible for calculating.
But in this code the Goal, itself is calculating the Head and Tail. I mean in my mind the code should have been like this (however it doesn't work!) :
sum(Tail, S1):-sum( [Elem | Tail], S),........
Because the goal is supposed to give the arguments and the right side is in charge of calculating.
2: I cannot understand how this code works step by step. can anyone give me a very simple example like how it calculates the sum of [1,2,3]?
In the left side of :- you have a head of the rule; on the right side you have the body of the rule. When the :- side and the body are missing, the rule becomes a fact.
It is not correct to say that the calculation is performed only in the body, because the decision making process of Prolog works with both sides of the rule. The concept where the head of the rule plays an important role is unification, a process by which the language decides which clauses are applicable to the query, and makes checks and temporary assignments of variables in the rule head to parts of the query ("unifies" them).
For example, when you query sum([1,2,3], X), Prolog checks both sum clauses, and decides that the query unifies only with the second one, because [] cannot be unified with [1,2,3].
Now it needs to unify [1,2,3] with [Elem | Tail] by making a temporary assignment that lasts for as long as we are in the body of the rule: Elem=1, and Tail = [2,3]. At this point it tries to solve sum again, passing [2,3] as the first parameter. The first rule does not match, so another temporary assignment of Elem=2 and Tail=[3] is made. In the third level of recursion we reach an assignment Elem=3 and Tail=[]. This is when we hit the first rule, producing an assignment of S1=0. Third level of invocation adds 3 to it; second level adds 2. First level adds 1, and returns with X set to 6.
An important part of prolog is matching (more generally, unification). When the Prolog run-time encounters a goal like sum(X,Y), it will try to match that term with the left-hand sides (head) of the rules for sum, in the order in which they appear. If the first rule fails, then the system will move to the second rule and so on.
In this case, the first head will only match if X is the empty list. If it is not empty, the match fails, and the next head is tried. This will succeed as long as X is any non empty list. Not only will it succeed, but it will bind the first element of the list to Elem , the rest of the list (which may be empty) to Tail. Since the second argument in the head of this rule is a variable, it will bind to whatever Y is.
Let's work through some examples:
sum([],X)?
First head matches, binding X to 0.
sum([1],X)?
First head does not match because [1] does not match []. Second one does with Elem <- 1, Tail<-[]. Therefore, we can proceed with the right-hand side of the rule:
sum(Tail,S1), S is S1 + Elem
Since Tail<-[] , the goal sum(Tail,S1) will yield the binding S1<-0 (see above). So with Elem<-1 and S1<-0, S1+Elem = 1.
And so on. Hopefully there is enough here for you to do the rest.
Your speculations about what happens during the execution are rather strange. No functions are involved at all.
For the evaluation of the goal sum([1,2,3], X), the second clause is selected because there is no matching between [1,2,3] (first argument of the goal) and [] (in the head of the first clause).
The matching instantiates Elem=1, Tail=[2,3] and S = X. Then the goal sum([2,3], S1) is evaluated, and succeeds (after recursion), returning the substitution S1=5. Then S=5+1 binds S to 6.

What does this line of code do exactly? (Prolog)

My lecturer gave us this sample program to look at the code, while I understood the recursive function on a whole the was this one line I couldn't quite grasp the meaning of
all_different([H | T]) :- member(H, T), !, fail.
extracted from the recursive function:
all_different([H | T]) :- member(H, T), !, fail.
all_different([_ | T]) :- all_different(T).
all_different([_]).
all I understood about it is that it splits a list into a Head H and a Tail T and checks if H is contained in T...My question is, what is it that "!" and "fail" do?
These things are pretty fundamental to Prolog.
fail is essential. It forces Prolog to consider the current branch a failure and initiates backtracking.
The ! is called "the cut." It commits Prolog to the current branch. Or, it prunes the trail of choice points under the current rule.
Taken in conjunction, in Prolog-ese, this says "If the head of the list is present in the tail of the list, there is no need to look for any additional answers, and fail." Thus, if any element of the list is present in the remainder of the list, you'll get an immediate failure with no chance of backtracking. This isn't actually all that dire, it just means that Prolog won't waste any more time trying to figure out if the list is "all_different." Backtracking will resume at the call site normally.
It's important that these go in this order. If you tried to cut after the fail, you'd never make it to the cut, because backtracking would already have begun. If you omit the cut, the predicate will return true if there is any sublist of the list which satisfies the property. This is guaranteed to be the case for any non-empty list by the last clause, which asserts that a list with one element satisfies the property. If you omit the fail, you're just going to get one success for each element of the list that is in a sublist, plus one for the tail. I encourage you to try playing around with the predicate, making these changes and seeing the effects, because it will go a long way to illustrating the purpose of the cut and fail.

Deeper Explanation of Prolog Count

Currently playing around in Prolog... I'm having trouble groking the count list rule. I haven't been able to find a good explanation anywhere. Can someone give me a break down of it at each recursion?
count(0, []).
count(Count, [Head|Tail]) :-
count(TailCount, Tail),
Count is TailCount + 1.
One place says that it is recursive (which makes sense to me) and another that says it isn't.
The procedure it's recursive, but not tail recursive. Writing tail recursive procedures is an optimization that allows the system to transform recursion into iteration, avoiding useless stack usage for deterministics computations (like the one we are speaking of).
In this case (that BTW it's the same of the builtin length/2, just with arguments swapped), we can use an accumulator, and rewrite the procedure in this way:
count(C, L) :- count(0, C, L).
count(Total, Total, []).
count(SoFar, Count, [_Head|Tail]) :-
Count1 is SoFar + 1,
count(Count1, Count, Tail).
Some system older required a cut before the recursive call, to make the optimization effective:
...,
!, count(Count1, Count, Tail).
The definition of the inference rule is recursive.
This program tries to count the quantity of elements inside the list.
count(0, []). This is an axiom, a fact, something that its true because you said so. Here you are stating that every empty list has a count of zero.
count(Count, [Head|Tail]) :-
count(TailCount, Tail),
Count is TailCount + 1.
This is an inference rule, that it a rule that dictates that the left part of :- is true if the right part is true. This inference rule also uses pattern matching, wicth matchs non empty lists ([Head|Tail]).
Specifically, the count rule says that the Count variable of a non empty list is the count of the Tail part of the list, plus 1 (the plus 1 is for counting the Head element of the list).

Resources