Related
I have a simple program I'm trying to write in Prolog. Essentially, as I learning exercise, I'm trying to write a program that takes two sorted lists as input, and returns the merged list that is also sorted. I have dubbed the predicate "merge2" as to no be confused with the included predicate "merge" that seems to do this already.
I am using recursion. My implementation is below
merge2([],[],[]).
merge2([X],[],[X]).
merge2([],[Y],[Y]).
merge2([X|List1],[Y|List2],[X|List]):- X =< Y,merge2(List1,[Y|List2],List).
merge2([X|List1],[Y|List2],[Y|List]):- merge2([X|List1],List2,List).
When I run this, I get X = [1,2,4,5,3,6] which is obviously incorrect. I've been able to code multiple times and tried to draw out the recursion. To the best of my knowledge, this should be returning the correct result. I'm not sure why the actualy result is so strange.
Thank you.
QuickCheck is your friend. In this case, the property that you want to verify can be expressed using the following predicate:
sorted(L1, L2) :-
sort(L1, S1),
sort(L2, S2),
merge2(S1, S2, L),
sort(L, S),
L == S.
Note that sort/2 is a standard Prolog built-in predicate. Using the QuickCheck implementation provided by Logtalk's lgtunit tool, which you can run using most Prolog systems, we get:
?- lgtunit::quick_check(sorted(+list(integer),+list(integer))).
* quick check test failure (at test 2 after 0 shrinks):
* sorted([0],[0])
false.
I.e. you code fails for L1 = [0] and L2 = [0]:
?- merge2([0], [0], L).
L = [0, 0] ;
L = [0, 0] ;
false.
Tracing this specific query should allow you to quickly find at least one of the bugs in your merge2/4 predicate definition. In most Prolog systems, you can simply type:
?- trace, merge2([0], [0], L).
If you want to keep duplicates in the merged list, you can use the de facto standard predicates msort/2 in the definition of the property:
sorted(L1, L2) :-
sort(L1, S1),
sort(L2, S2),
merge2(S1, S2, L),
msort(L, S),
L == S.
In this case, running QuickCheck again:
?- lgtunit::quick_check(sorted(+list(integer),+list(integer))).
* quick check test failure (at test 3 after 8 shrinks):
* sorted([],[475,768,402])
false.
This failure is more informative if you compare the query with your clauses that handle the case where the first list is empty...
This is done using difference list and since you are learning it uses reveals, AKA spoiler, which are the empty boxes that you have to mouse over to ravel the contents. Note that the reveals don't allow for nice formatting of code. At the end is the final version of the code with nice formatting but not hidden by a reveal so don't peek at the visible code at the very end if you want to try it for yourself.
This answer takes it that you have read my Difference List wiki.
Your basic idea was sound and the basis for this answer using difference list. So obviously the big change is to just change from closed list to open list.
As your code is recursive, the base case can be used to set up the pattern for the rest of the clauses in the predicate.
Your simplest base case is
merge2([],[],[]).
but a predicate using difference list can use various means to represent a difference list with the use of L-H being very common but not one I chose to use. Instead this answer will follow the pattern in the wiki of using two variables, the first for the open list and the second for the hole at the end of the open list.
Try to create the simple base case on your own.
merge2_prime([],[],Hole,Hole).
Next is needed the two base cases when one of the list is empty.
merge2_prime([X],[],Hole0,Hole) :-
Hole0 = [X|Hole].
merge2_prime([],[Y],Hole0,Hole) :-
Hole0 = [Y|Hole].
Then the cases that select an item from one or the other list.
merge2_prime([X|List1],[Y|List2],Hole0,Hole) :-
X =< Y,
Hole0 = [X|Hole1],
merge2_prime(List1,[Y|List2],Hole1,Hole).
merge2_prime(List1,[Y|List2],Hole0,Hole) :-
Hole0 = [Y|Hole1],
merge2_prime(List1,List2,Hole1,Hole).
Lastly a helper predicate is needed so that the query merge2(L1,L2,L3) can be used.
merge2(L1,L2,L3) :-
merge2_prime(L1,L2,Hole0,Hole),
Hole = [],
L3 = Hole0.
If you run the code as listed it will produce multiple answer because of backtracking. A few cuts will solve the problem.
merge2(L1,L2,L3) :-
merge2_prime(L1,L2,Hole0,Hole),
Hole = [],
L3 = Hole0.
merge2_prime([],[],Hole,Hole) :- !.
merge2_prime([X],[],Hole0,Hole) :-
!,
Hole0 = [X|Hole].
merge2_prime([],[Y],Hole0,Hole) :-
!,
Hole0 = [Y|Hole].
merge2_prime([X|List1],[Y|List2],Hole0,Hole) :-
X =< Y,
!,
Hole0 = [X|Hole1],
merge2_prime(List1,[Y|List2],Hole1,Hole).
merge2_prime(List1,[Y|List2],Hole0,Hole) :-
Hole0 = [Y|Hole1],
merge2_prime(List1,List2,Hole1,Hole).
Example run:
?- merge2([1,3,4],[2,5,6],L).
L = [1, 2, 3, 4, 5, 6].
?- merge2([0],[0],L).
L = [0, 0].
I didn't check this with lots of examples as this was just to demonstrate that an answer can be found using difference list.
I'm trying to understand prolog but I am stuck with one example, can you explain to me how is prolog going through this call:
eli(2,[2,2,1],L).
using those facts:
eli(X,[],[]).
eli(X,[Y],[Y]).
eli(X,[X,X|L],L1) :- eli(X,L,L1).
eli(X,[Y|L],[Y|L1]) :- eli(X,L,L1).
The results are:
L = [1]
L = [1]
L = [2, 2, 1]
L = [2, 2, 1]
and I'm not really sure why.
Thanks in advance!
It looks like your predicate is mean to delete two consecutive appearance of any element.
First clause, if the target list is empty, return the empty list. In this case the X variable in the fact is not necessary. Replace X by te anonymous variable.
eli(_,[],[]).
Second clause is similar to the first, but it matches the target list if it contains only one element. Variable X is also not necessary.
eli(_,[Y],[Y]).
Third clause, if the target list contains two or more elements, and in the Head of the list both elements are equal to X, don't copy this two elements to the Result list, and make a recursive call to the eli/3 predicate in the body of the rule, to continue the search.
eli(X,[X,X|L],L1) :- eli(X,L,L1), !.
In this case we add the cut predicate, to avoid backtracking after this rule succeeded. Otherwise you may get undesired results, like L = [2, 2, 1] in your test.
And the last clause, copy the element in the Head of the Target list to the Result list, and continue the recursive call, this will stop when the Target list is empty or contains only one element (your first two clauses).
eli(X,[Y|L],[Y|L1]) :- eli(X,L,L1).
Now this is your predicate eli/3:
eli(_,[],[]).
eli(_,[Y],[Y]).
eli(X,[X,X|L],L1) :- eli(X,L,L1), !.
eli(X,[Y|L],[Y|L1]) :- eli(X,L,L1).
Test:
?- eli(2,[2,2,1],L).
L = [1]
?- eli(2,[1,2,2,3],L).
L = [1, 3]
I'm confusing myself and it'll be helpful if someone can point me in the right direction. I need to get duplicates out of a nested list. I thought I could simply find out how to get duplicates out of a regular list and then make a rule for getting subsets then somehow combine them and it'll work but I think I'm confusing myself more by doing that.
Here's what I have so far, it deletes the duplicates fine.
Removes Duplicates:
duplicate([],[]).
duplicate([H|T],C) :- var(H,T),!, duplicate(T,C).
duplicate([H|T],[H|C]) :- duplicate(T,C).
var(X,[H|_]) :- X==H,!.
var(X,[_|T]) :- var(X,T).
Subset Rule:
subset([],_).
subset([H|T],L):- member(H,L),subset(T,L).
currently if I call duplicate([1,2,2,3,4,a,a,a,b,b,b], X). it'll return X = [1,2,3,4a,b] which is correct but I want to be able to call duplicate([1,[2,[2,[1,[a,[a]]]]]], X). and have it return X = [1,2,a]
Is my thought process correct or am I thinking of this incorrectly?
You can just flatten the list as a preprocessing step:
?- flatten([1,[2,[2,[1,[a,[a]]]]]], L).
L = [1, 2, 2, 1, a, a].
And then use your existing duplicate on the flatten list: flatten([1,[2,[2,[1,[a,[a]]]]]], L), duplicate(L, X).
I don't quite understand whats happening the this code:
reverse2([],[]).
reverse2([H|T],R):- reverse2(T,R2), append(R2,[H],R).
It doesn't make sense to me that we are recursing before appending.
Could someone explain it how each element H is being appending after the base case is reached?
Thanks.
The recursion is on the tail of the list. Consider the list [1,2,3]. The first rule doesn't match. The second rule matches, unifying H = 1 and T = [2,3]. Then we call reverse2([2,3], R2). Again, the first rule doesn't match. The second rule matches, unifying H = 2 and T = [3]. You can see that recursion from here will eventually hit the first rule. Zooming back to the outermost call where H = 1 and T = [2, 3], we will wind up with R2 = [3, 2]. Then the append will occur, sticking [1] on the end.
You may find it instructive to do a sample query like this:
?- trace, reverse2([1,2,3], X).
This will show you how the query unfolds and the bindings of each variable.
The way to think about recursive functions is inductively. Look at the base case. The base case should be trivially true, and it is--the reverse of the empty list is indeed the empty list. Then look at the inductive case. Assuming that it works for a smaller list of size N, does it work for a list of size N+1? Assume it can reverse the tail of the list (size = N), and see that if that is true, appending the head to the end would make it work for N+1. That's all you need to believe for induction to work. If you believe those two things, you believe everything there is to believe and it will work for every input. So liberate yourself from the need to believe in any other steps. :)
Just to build a little on Daniel's description, you could read the recursive clause:
reverse2([H|T], R) :- reverse2(T, R2), append(R2, [H], R).
As:
R is the reverse of list [H|T] if R2 is the reverse of T, and R is the list [H] appended to the end of list R2.
Or, somewhat imperatively, as:
To reverse a list, first reverse its tail, then append the head of the original list to that result.
So in that description, the recursive goal of reverse2(T, R2) comes first.
The definition in this case is depth first. It's going to keep recursing until it hits the trivial base case, then return from each goal doing the appends:
(1) reverse2([1,2,3], R) :- reverse2([2,3], R2), append(R2, [1], R).
(2) reverse2([2,3], R) :- reverse2([3], R2), append(R2, [2], R).
(3) reverse2([3], R) :- reverse2([], R2), append(R2, [3], R).
(4) reverse2([], []).
Then the returns:
(3) reverse2([3], R) :- reverse2([], []), append([], [3], R). % R = [3]
(2) reverse2([2,3], R) :- reverse2([3], [3]), append([3], [2], R). % R = [3,2]
(1) reverse2([1,2,3], R) :- reverse2([2,3], [3,2]), append([3,2], [1], R).
Result:
reverse2([1,2,3], [3,2,1]).
As an aside, this is actually a fairly inefficient way to do a reverse/2 predicate. And it has some issues if you do, reverse(L, [1,2,3]) after it finds the first solution. It's interesting to note that if you swap the recursion and the append/3 queries around:
reverse2([H|T], R) :- append(R2, [H], R), reverse2(T, R2).
This actually behaves better on reverse2(L, [1,2,3]) but then has a problem after finding the first solution to reverse2([1,2,3], L). The opposite of what happens with the case in which the recursive query comes first. With this definition, if the first argument is bound but not the second, it has the inefficiency of finding irrelevant possible solutions to the append(R2, [H], R) first when neither R2 or R are initially bound before it finds the correct solution through the subsequent recursion. Then after finding that solution, it infinitely tries more irrelevant potential solutions emerging from backtracks to append/3.
If you look up the source code for reverse/2 in the SWI Prolog implementation, it uses pure recursion and difference lists to overcome these shortcomings.
i have to get list difference between two integer list (both ordinate).
i white this:
difference(L,[],L) :- !.
difference([],_,[]) :- !.
difference([],[],W).
difference([H|T1],[D|T2],T3) :- difference(T1,[D|T2],[H|T3]).
difference([H|T1],[H|T2],T3) :- difference(T1,T2,T3).
but why i can't get my list difference?
if i write this:
difference([],[],W):- write(X).
and this example:
| ?- difference([1,4,4],[1,4],R).
[4|_27]
it makes right!
NB if i have duplicate number i have to show it!
I find your code rather odd. For instance, your third clause: what's W for? Seems like you mean to say:
difference([],[],_).
Second problem: in the fourth clause, there's nothing stopping H and D from being independent variables with the same binding. I suspect you mean something like this:
difference([H|T1],[D|T2],T3) :- H \= D, difference(T1,[D|T2],[H|T3]).
Fixing these things seems to fix the predicate to give a reasonable looking answer:
| ?- difference([1,4,4], [1,4], R).
R = [4]
I think your first several clauses are trying to handle different sorts of base cases, is that right? E.g.:
difference(L, [], L) % handles the case where the second list is exhausted
difference([], _, []) % handles the case where the first list is exhausted
difference([], [], W) % handles the case where the lists are exhausted at the same time
One problem with this is that L = [] is a legitimate binding, so the first and third clauses mean the same thing. You can probably safely remove the third one, because it would have matched and produced the same answer on the first. The second clause is more interesting, because it seems to say that regardless of whatever work we've done so far, if the first list is empty, the result is empty. I find that possibility a bit jarring--is it possible you actually want these two base cases? :
difference([], L, L).
difference(L, [], L).
I remain unconvinced, but until I have a better idea what you're trying to accomplish I may not be able to help more. For instance, what should happen with difference([1, 4], [1, 4, 4], R)? I posit you probably want R = [4], but your code will produce R = [].
Also, I find it unlikely that
difference([],[],W):- write(X).
is going to be a helpful debugging strategy, because Prolog will generate a new variable binding for X because there's nothing for it to refer to.
The final version I have with all my changes looks like this:
difference(L, [], L) :- !.
difference([], L, L) :- !.
difference([H|T1], [D|T2], T3) :- D \= H, difference(T1, [D|T2], [H|T3]).
difference([H|T1], [H|T2], T3) :- difference(T1, T2, T3).
Edit: does this implement your requirements?
not_in1(X, Left, Right) :- member(X, Left), \+ member(X, Right).
not_in(X, Left, Right) :- not_in1(X, Left, Right).
not_in(X, Left, Right) :- not_in1(X, Right, Left).
differences(Left, Right, Differences) :-
findall(X, not_in(X, Left, Right), Differences).
?- differences([1,2,3,4], [1,3,5], X).
X = [2,4,5]
If so, I'll try to get your original code to produce answers that match.
Edit 2: OK, so the problem with the solution above is that it is O(N^2). In the worst case (two totally distinct lists) it will have to compare every item from list 1 to every item of list 2. It's not exploiting the fact that both lists are ordered (I believe that's what you mean by 'ordinate').
The result looks a lot more like your original code, but your original code is not taking advantage of the fact that the items are ordered. This is why the fourth and fifth cases are confusing looking: you should recur down one of the lists or the other depending on which number is larger. The corrected code looks like this:
differences([], Result, Result).
differences(Result, [], Result).
differences([H|Ls], [H|Rs], Result) :- differences(Ls, Rs, Result).
differences([L|Ls], [R|Rs], [L|Result]) :-
L < R,
differences(Ls, [R|Rs], Result).
differences([L|Ls], [R|Rs], [R|Result]) :-
L > R,
differences([L|Ls], Rs, Result).
You can see this produces the same result as the O(N^2) method:
?- differences([1,2,3,4], [1,3,5], X).
X = [2,4,5]
You were right, you do need both base cases. This is so the remainder of either list becomes part of the result. Presumably these will be the largest values ([5] in the example).
Now I have three inductive cases: one for <, one for > and one for =. The equality case is intuitive: recur on both lists, discarding the head of both lists. The next case basically says if the left head is less than the right head, add it to the result and recur on the left's tail. The right is unchanged in that case. The other case is the mirror of this case.
Hope this helps!