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.
Last time I learnt about =.. that can translate a list to term and opposite.
I have 3 predicates to do, first one is the one that translates a list to a term. I came up with sth like this:
list_to_term(List, Functor, Term) :-
Term =.. [Functor | List].
Is it okey? Enough? Or I miss something?
The other predicate is count(A,T,N) for element A, in term T with number N that is true if N is a count of elements A in term T... Can anyone help me with this one or how to start?
?- count(a,f(a),N).
N = 1
?- count(a,f(a,g(b,a),N).
N = 2.
?- count(a,f(a,g(X,a),N).
N = 2.
Looking at the answer of this post you can reuse the predicate flatten_term/2, a little bit modified to handle free variables, to sove your problem. Here is the code for a basic solution:
flatten_term(Term,[Term]):-
(atomic(Term);var(Term)),!.
flatten_term(Term,Flat):-
Term =.. TermList,
flatten_term_list(TermList,Flat),!.
flatten_term_list([],[]):-!.
flatten_term_list([H|T],List):-
flatten_term(H,HList),
flatten_term_list(T,TList),
append(HList,TList,List),!.
occurrences(_,[],N,N):-!.
occurrences(A,[H|T],N,Tot):-
A \== H,!,
occurrences(A,T,N,Tot).
occurrences(A,[H|T],N,Tot):-
A == H,!,
N1 is N+1,
occurrences(A,T,N1,Tot).
count(A,Term,N):-
flatten_term(Term,Flatten),
occurrences(A,Flatten,0,N).
?- count(a,f(a,g(X,a),d),T).
T = 2.
?- count(X,f(a,g(X,a),d),T).
T = 1
First of all you flatten the term using flatten_term/2. Then simply count the occurrences of the element you want to find using occurrences/4. You can, if you want, modify flatten_term/2 to avoid the usage of occurrences/4 and so scan the term (list) only one time... Something like: flatten_term(Term,Flatten,ElementToFind,Counter,Total).
Start by solving a more general problem of counting the terms in a list. Processing a term is processing a singleton list containing that term, after all:
count(A,T,N):- count(A, [T|Z],Z, 0,N).
count(_, [], [], C,N):- N is C, !.
count(A, [T|B],Z, C,N):- ?=(A,T), A=T, !, count(A, B,Z, C+1,N).
count(A, [T|B],Z, C,N):- ?=(A,T), T=..[_|S], !, append(S,Y,Z), count(A, B,Y, C,N).
count(A, [_|B],Z, C,N):- count(A, B,Z, C,N).
This opens up each head term in a list in succession and appends its argument terms to that list thus using it as a queue... thus processing the predicate's second argument T in a breadth-first manner.
This assumes A argument is an atom, and ?= is used to avoid instantiating the free variables we might encounter, and instead to skip over them, as your examples seem to indicate.
Is it okey? Enough? Or I miss something?
Prolog's =../2 predicate [swi-doc] can "pack" and "unpack" a list that contains the functor name and its arguments in a term and vice versa. So one can use this to construct a term, or to analyze a term. For example:
?- f(a,g(b,a)) =.. L.
L = [f, a, g(b, a)].
Here f is the functor name, and a and g(b, a) are the arguments. These arguments can be terms as well, and then we thus need to unpack these arguments further.
We can for example obtain all the subterms of a term with:
subterms(T, T) :-
\+ var(T).
subterms(T, ST) :-
\+ var(T),
T =.. [_|As],
member(A, As),
subterms(A, ST).
For example:
?- subterms(f(a,g(X,a)),N).
N = f(a, g(X, a)) ;
N = a ;
N = g(X, a) ;
N = a ;
false.
Now that we obtained all (sub)terms, we can slightly rewrite the predicate to count the number of elements that match:
subterm_query(Q, T) :-
Q == T.
subterm_query(Q, T) :-
\+ var(T),
T =.. [_|As],
member(A, As),
subterm_query(Q, A).
so we obtain if we query for a:
?- subterm_query(a, f(a,g(X,a))).
true ;
true ;
false.
If we can use the aggregate library, we can make use of the aggregate_all/3 predicate to count the number of times, the predicate was succesful:
?- aggregate_all(count, subterm_query(a, f(a,g(X,a))), Count).
Count = 2.
If not, you need to implement a mechanism that returns 1 for a match, and sums up recursively the matches of the child terms. I leave this as an exercise.
I'm currently learning prolog and doing the 99 prolog excersises thing.
I've got a question to compress a list, such that all duplicate elements should be removed.
Desired outcome:
?- compress([a,a,a,a,b,c,c,a,a,d,e,e,e,e],X).
X = [a,b,c,d,e]
My code currently looks like:
compress([],[]).
compress([H|T], X) :-
( \+ member(H, X) ->
append([H],X,X)
), compress(T,X).
By running this with trace it always fails in after the first append, and returns false.
Does anyone have an idea on how to solve this?
append([H],X,X) - this line is nonsense. If X is a finite list, you cannot prepend a non-empty list to it and still get the same list X.
Prolog programs are statements of fact and deduction rules, like a mathematical proof, not imperatives like x = [h] + x. There are no "variables" in the same sense as in Java/C++/JavaScript programs.
In particular, compress([H|T], X) :- foo, compress(T,X). also looks weird, because you say that the result of compressing list [H|T] (namely, X) is the same as the result of compressing list T (X as well), but only if foo is correct.
Also, if you want to get [a,b] for [a,b,a], then you probably have to add extra predicates. Predicate cannot "remember" anything (e.g. it cannot "iterate over all items and only get the first occurrence). It can only make recursive calls.
If you're ok with keeping the last occurrence only, I'd write it the following way:
compress([],[]). % Empty list
compress([H|T], X) :-
member(H, T), % If H is not the last occurrence...
compress(T, X). % remove duplicates from the tail, ignore H.
compress([H|T], X) :-
not(member(H, T)), % If H is the last occurrence...
compress(T, XT), % ...compress the tail...
append([H], XT, X). % ...and prepend H to the result.
?- append([], [X1], [a,b]).
Why does this return no and not
X1 = a,b
Since
? - append([], [a,b], [a,b])
returns yes?
To understand a Prolog program you have two choices:
Think about the program as you do this in other programming languages by simulating the moves of the processor. This will lead to your mental exasperation very soon unless your name is Ryzen or in other words:
You are a processor
Let Prolog do the thinking and use Prolog to understand programs.
Whenever you see a failing goal, narrow down the reason why the goal fails by generalizing that goal (by replacing some term by a variable). You do not need to understand the precise definition at all. It suffices to try things out. In the case of your query
?- append([], [X1], [a,b]).
false.
We have three arguments. Maybe the first is the culprit? So I will replace it by a new variable:
?- append(Xs, [X1], [a,b]).
Xs = [a], X1 = b
; false.
Nailed it! Changing the first argument will lead to success. But what about the second argument?
?- append([], Ys, [a,b]).
Ys = [a, b].
Again, culprit, too. And now for the third:
?- append([], [X1], Zs).
Zs = [X1].
Verdict: All three kind-of guilty. That is, it suffices to blame one of them. Which one is up to you to choose.
Do this whenever you encounter a failing goal. It will help you gain the relational view that makes Prolog such a special language.
And if we are at it. It often helps to consider maximal failing generalizations. That is, generalizations that still fail but where each further step leads to success. In your example this is:
?- append([], [X1], [a,b]). % original query
false.
?- append([], [_], [_,_|_]). % maximal failing generalization
false.
From this you can already draw some conclusions:
The lists' elements are irrelevant.
Only the length of the three lists is of relevance
The third list needs to be two elements or longer.
See: append/3
append(?List1, ?List2, ?List1AndList2)
List1AndList2 is the concatenation of List1 and List2
So for
?- append([], [X1], [a,b]).
[] is the empty list and [X1] is a list with a variable X1
If you run the query like this
?- append([],[X1],A).
you get
A = [X1].
which means that A is the concatenation of [] and [X1].
In your query it is asking if the concatenation of [] and [X1] is [a,b] which is false, or no.
For your second query
? - append([], [a,b], [a,b])
it is asking if the concatenation of [] and [a,b] is [a,b] which is true, or yes.
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!