Related
I want to implement a predicate P(Xs,Ys,Zs) where Xs,Ys,Zs are lists.
I'm new in Prolog and I can't find a way to get to the longest sequence in Xs (example. Xs = ['b','b','A','A','A','A','b','b']) which is included to Ys (for example Ys = ['A','A','A','A','c','A','A','A','A']) without crossing- an even number of times. Maybe someone already wrote this code ore some one can say me how can I start. Thanks for helps.
explanation of teacher.
longest_subsequence(List, Part, Subsequence):-
longest_subsequence_(List, Part, [], Subsequence).
longest_subsequence_(Xs, Ys, CurrentSubsequence, LongestSubsequence):-
append(CurrentSubsequence, Ys, NextSubsequence),
divide_list(Xs, [_LeftYs, NextSubsequence, _RightYs]), !,
longest_subsequence_(Xs, Ys, NextSubsequence, LongestSubsequence).
longest_subsequence_(_Xs, _Ys, LongestSubsequence, LongestSubsequence).
okey i did.
main_task(Xs, Ys, Zs) :-
atom_chars(Xs, Xl),
atom_chars(Ys, Yl),
retractall(record(_, _)),
assert(record(0, [])),
process(Xl, Yl, Zl),
atom_chars(Zs, Zl).
process(Xl, Yl, _) :-
get_sublist(Xl, Zl),
length(Zl, L),
record(MaxL, _),
L > MaxL,
get_index(Yl, Zl, Il),
test_even(Il),
test_intersect(Il, L),
retractall(record(_, _)),
assert(record(L, Zl)),
fail.
process(_, _, Zl) :-
record(_, Zl).
get_sublist(L1, L2) :-
get_tail(L1, L3),
get_head(L3, L2).
get_tail(L, L).
get_tail([_|T], L) :-
get_tail(T, L).
get_head([H|T1], [H|T2]) :-
get_head(T1, T2).
get_head(_, []).
get_index(Yl, Zl, Il) :-
get_index(Yl, Zl, Il, 0).
get_index([], _, [], _).
get_index([Yh|Yt], Zl, [I|It], I) :-
get_head([Yh|Yt], Zl),
!,
I1 is I + 1,
get_index(Yt, Zl, It, I1).
get_index([_|Yt], Zl, Il, I) :-
I1 is I + 1,
get_index(Yt, Zl, Il, I1).
test_even(Il) :-
length(Il, L),
L > 0,
L mod 2 =:= 0.
test_intersect([_], _).
test_intersect([X,Y|T], L) :-
Y - X >= L,
test_intersect([Y|T], L).
All lines in the list at the symbols on working with lists
Initialize the dynamic database - will be stored in it, and its maximum line length
enumerates all of the substring (sublists) from X. Bust goes double "pruning" - first place in a list of cut off the front, then from behind.
Check the length of the resulting string, if we already have a long, immediately leave for the continuation of busting
We consider a list of indexes in the occurrence of a Y, then there is every element of the list - a position in the Y, from which it includes Z.
Check the parity - just consider the length of the list of indexes, chёtnaya length - an even number of entries. And we need to check that it is greater than zero.
Check the intersection - you need to check the difference between two adjacent elements of the list of indexes, the difference should always be greater than the length Z.
If all checks are made, there is a dynamic database updates - current list Z is stored as the maximum
At the end it is a forced failure, it is rolled back to the fork in paragraph 3) and the continued search.
Note: If any check is not performed, the failure of this test is immediately rolled back to the fork in paragraph 3) and the continued search.
When the bust comes to an end, performed a second rule predicate process, it simply selects the last spicok Z in the base.
At the end of the list Z is converted back to a string
A naive approach is the following:
longest_subsequence(Xs,Ys,Zs) :-
longest_subsequence(Xs,Ys,Ys,0,[],Zs).
longest_subsequence([X|Xs],Y0,[Y|Ys],N0,Z0,Z) :-
try_seq([X|Xs],[Y|Ys],Nc,Zc),
(Nc > N0
-> longest_subsequence([X|Xs],Y0,Ys,Nc,Zc,Z)
; longest_subsequence([X|Xs],Y0,Ys,N0,Z0,Z)
).
longest_subsequence([_|Xs],Y0,[],N0,Z0,Z) :-
longest_subsequence(Xs,Y0,Y0,N0,Z0,Z).
longest_subsequence([],_,_,_,Z,Z).
try_seq([H|TA],[H|TB],N,[H|TC]) :-
!,
try_seq(TA,TB,N1,TC),
N is N1+1.
try_seq(_,_,0,[]).
here a predicate try_seq/3 aims to match as much as possible (generate the longest common subsequence) starting from the beginning of the list.
The problem is that this is a computationally expensive approach: it will have a time complexity O(m n p) with n the length of the first list, m the length of the second list and p the minimum length of the two lists.
Calling this with your example gives:
?- longest_subsequence([b,b,a,a,a],[a,a,a,c,a,a,a],Zs).
Zs = [a, a, a] ;
false.
You can make the algorithm more efficient using back-referencing, this is more or less based on the Knuth-Morris-Pratt-algorithm.
When approaching a problem, first try: divide and conquer.
When we have a list_subsequence(+List, ?Subsequence) predicate
list_subsequence([H|T], S) :-
list_subsequence(H, T, S, _).
list_subsequence([H|T], S) :-
list_subsequence(H, T, _, R),
list_subsequence(R, S).
list_subsequence(H, [H|T], [H|S], R) :- !, list_subsequence(H, T, S, R).
list_subsequence(H, R, [H], R).
we can call for library(aggregate) help:
longest_subsequence(Seq, Rep, Longest) :-
aggregate(max(L, Sub), N^(
list_subsequence(Seq, Sub),
aggregate(count, list_subsequence(Rep, Sub), N),
N mod 2 =:= 0,
length(Sub, L)
), max(_, Longest)).
edit: more library support available
A recently added library helps:
longest_subsequence_(Seq, Rep, Longest) :-
order_by([desc(L)], filter_subsequence(Seq, Rep, Longest, L)), !.
where filter_subsequence/4 is simply the goal of the outer aggregate:
filter_subsequence(Seq, Rep, Sub, L) :-
list_subsequence(Seq, Sub),
aggregate(count, list_subsequence(Rep, Sub), N),
N mod 2 =:= 0,
length(Sub, L).
I have a strange problem that I do not know how to solve.
I have written a predicate that compresses lists by removing repeating items.
So if the input is [a,a,a,a,b,c,c,a,a], output should be [a,b,c,a]. My first code worked, but the item order was wrong. So I add a append/3 goal and it stopped working altogether.
Can't figure out why. I tried to trace and debug but don't know what is wrong.
Here is my code which works but gets the item order wrong:
p08([Z], X, [Z|X]).
p08([H1,H2|T], O, X) :-
H1 \= H2,
p08([H2|T], [H1|O], X).
p08([H1,H1|T], O, X) :-
p08([H1|T], O, X).
Here's the newer version, but it does not work at all:
p08([Z], X, [Z|X]).
p08([H1,H2|T], O, X) :-
H1 \= H2,
append(H1, O, N),
p08([H2|T], N, X).
p08([H1,H1|T], O, X) :-
p08([H1|T], O, X).
H1 is not a list, that's why append(H1, O, N) fails.
And if you change H1 to [H1] you actually get a solution identical to your first one. In order to really reverse the list in the accumulator you should change the order of the first two arguments: append(O, [H1], N). Also, you should change the first rule with one that matches the empty list p08([], X, X) (without it, the goal p08([], [], Out) fails).
Now, to solve your problem, here is the simplest solution (which is already tail recursive, as #false stated in the comments to this answer, so there is no need for an accumulator)
p([], []). % Rule for empty list
p([Head, Head|Rest], Out):- % Ignore the Head if it unifies with the 2nd element
!,
p([Head|Rest], Out).
p([Head|Tail], [Head|Out]):- % otherwise, Head must be part of the second list
p(Tail, Out).
and if you want one similar to yours (using an accumulator):
p08(List, Out):-p08(List, [], Out).
p08([], Acc, Acc).
p08([Head, Head|Rest], Acc, Out):-
!,
p08([Head|Rest], Acc, Out).
p08([Head|Tail], Acc, Out):-
append(Acc, [Head], Acc2),
p08(Tail, Acc2, Out).
Pure and simple:
list_withoutAdjacentDuplicates([],[]).
list_withoutAdjacentDuplicates([X],[X]).
list_withoutAdjacentDuplicates([X,X|Xs],Ys) :-
list_withoutAdjacentDuplicates([X|Xs],Ys).
list_withoutAdjacentDuplicates([X1,X2|Xs],[X1|Ys]) :-
dif(X1,X2),
list_withoutAdjacentDuplicates([X2|Xs],Ys).
Sample query:
?- list_withoutAdjacentDuplicates([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a] ; % succeeds, but leaves useless choicepoint(s) behind
false
Edit 2015-06-03
The following code is based on if_/3 and reified term equality (=)/3 by #false, which---in combination with first argument indexing---helps us avoid above creation of useless choicepoints.
list_without_adjacent_duplicates([],[]).
list_without_adjacent_duplicates([X|Xs],Ys) :-
list_prev_wo_adj_dups(Xs,X,Ys).
list_prev_wo_adj_dups([],X,[X]).
list_prev_wo_adj_dups([X1|Xs],X0,Ys1) :-
if_(X0 = X1, Ys1 = Ys0, Ys1 = [X0|Ys0]),
list_prev_wo_adj_dups(Xs,X1,Ys0).
Let's see it in action!
?- list_without_adjacent_duplicates([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a]. % succeeds deterministically
In this answer we use meta-predicate foldl/4 and
Prolog lambdas.
:- use_module(library(apply)).
:- use_module(library(lambda)).
We define the logically pure predicatelist_adj_dif/2 based on if_/3 and (=)/3:
list_adj_dif([],[]).
list_adj_dif([X|Xs],Ys) :-
foldl(\E^(E0-Es0)^(E-Es)^if_(E=E0,Es0=Es,Es0=[E0|Es]),Xs,X-Ys,E1-[E1]).
Let's run the query given by the OP!
?- list_adj_dif([a,a,a,a,b,c,c,a,a],Xs).
Xs = [a,b,c,a]. % succeeds deterministically
How about a more general query? Do we get all solutions we expect?
?- list_adj_dif([A,B,C],Xs).
A=B , B=C , Xs = [C]
; A=B , dif(B,C), Xs = [B,C]
; dif(A,B), B=C , Xs = [A,C]
; dif(A,B), dif(B,C), Xs = [A,B,C].
Yes, we do! So... the bottom line is?
Like many times before, the monotone if-then-else construct if_/3 enables us to ...
..., preserve logical-purity, ...
..., prevent the creation of useless choicepoints (in many cases), ...
..., and remain monotone—lest we lose solutions in the name of efficiency.
More easily:
compress([X],[X]).
compress([X,Y|Zs],Ls):-
X = Y,
compress([Y|Zs],Ls).
compress([X,Y|Zs],[X|Ls]):-
X \= Y,
compress([Y|Zs],Ls).
The code works recursevely and it goes deep to the base case, where the list include only one element, and then it comes up, if the found element is equal to the one on his right , such element is not added to the 'Ls' list (list of no duplicates ), otherwise it is.
compr([X1,X1|L1],[X1|L2]) :-
compr([X1|L1],[X1|L2]),
!.
compr([X1|L1],[X1|L2]) :-
compr(L1,L2).
compr([],[]).
There are many resources on how to remove duplicates and similar issues but I can't seem to be able to find any on removing unique elements. I'm using SWI-Prolog but I don't want to use built-ins to achieve this.
That is, calling remove_unique([1, 2, 2, 3, 4, 5, 7, 6, 7], X). should happily result in X = [2, 2, 7, 7].
The obvious solution is as something along the lines of
count(_, [], 0) :- !.
count(E, [E | Es], A) :-
S is A + 1,
count(E, Es, S).
count(E, [_ | Es], A) :-
count(E, Es, A).
is_unique(E, Xs) :-
count(E, Xs, 1).
remove_unique(L, R) :- remove_unique(L, L, R).
remove_unique([], _, []) :- !.
remove_unique([X | Xs], O, R) :-
is_unique(X, O), !,
remove_unique(Xs, O, R).
remove_unique([X | Xs], O, [X | R]) :-
remove_unique(Xs, O, R).
It should become quickly apparent why this isn't an ideal solution: count is O(n) and so is is_unique as it just uses count. I could improve this by failing when we find more than one element but worst-case is still O(n).
So then we come to remove_unique. For every element we check whether current element is_unique in O. If the test fails, the element gets added to the resulting list in the next branch. Running in O(n²), we get a lot of inferences. While I don't think we can speed it in the worst case, can we do better than this naïve solution? The only improvement that I can clearly see is to change count to something that fails as soon as >1 elements are identified.
Using tpartition/4 in tandem with
if_/3 and (=)/3, we define remove_unique/2 like this:
remove_unique([], []).
remove_unique([E|Xs0], Ys0) :-
tpartition(=(E), Xs0, Es, Xs),
if_(Es = [], Ys0 = Ys, append([E|Es], Ys, Ys0)),
remove_unique(Xs, Ys).
Here's the sample query, as given by the OP:
?- remove_unique([1,2,2,3,4,5,7,6,7], Xs).
Xs = [2,2,7,7]. % succeeds deterministically
As long as you don't know that the list is sorted in any way, and you want to keep the sequence of the non-unique elements, it seems to me you can't avoid making two passes: first count occurrences, then pick only repeating elements.
What if you use a (self-balancing?) binary tree for counting occurrences and look-up during the second pass? Definitely not O(n²), at least...
I have defined a goal lowerpartition/3 as follows:
lowerpartition(X,P,Z) :- var(Z),!,lowerpartition(X,P,[]).
lowerpartition([],_,_).
lowerpartition([X|Xs],P,Z) :- X=<P, lowerpartition(Xs,P,[X|Z]).
lowerpartition([X|Xs],P,Z) :- X>P, lowerpartition(Xs,P,Z).
when I call
lowerpartition([1,2,3,4,5],3,X).
I expect X to be bound to the list [3,2,1], but Prolog just returns false. What am I doing incorrectly?
It seems that you are mixing an accumulator-based approach with a stack based approach.
Your first clause:
lowerpartition(X,P,Z) :- var(Z),!,lowerpartition(X,P,[]).
will leave Z uninstantiated, it is not used after checking that it is a variable therfore it won't be unified...
Try this:
lowerpartition([], _, []).
lowerpartition([X|Xs], P, [X|Zs]):-
X =< P, lowerpartition(Xs, P, Zs).
lowerpartition([X|Xs], P, Zs):-
X > P, lowerpartition(Xs, P, Zs).
Because you use a predicate that prolog cant unify in the first clause.
lowerpartition(X,P,Z) :- var(Z),
!,
lowerpartition(X,P,[]). % here is what prolog cant unify
A little modification to the code :
lowerpartition(X,P,Z) :- var(Z),lowerpartition_1(X,P,Z),!. % note the position of cut aswell
lowerpartition_1([],_,[]).
lowerpartition_1([X|Xs],P,[X|Z]) :- X=<P, lowerpartition_1(Xs,P,Z).
lowerpartition_1([X|Xs],P,Z) :- X>P, lowerpartition_1(Xs,P,Z).
Hope this helps.
Here a DCG based solution: my simple minded test return the same results as gusbro solution.
lowerpartition(P), [X] --> [X], {X=<P}, lowerpartition(P), !.
lowerpartition(P) --> [X], {X>P}, lowerpartition(P).
lowerpartition(_) --> [].
here is how to call it:
?- phrase(lowerpartition(3), [1,2,3,4,5,3,2,6,7], X).
X = [1, 2, 3, 3, 2].
but if you are using a Prolog with lìbrary(apply), then
lowerpartition(Xs, P, Rs) :- exclude(compare(<, P), Xs, Rs).
returns the same result as above
I am completely new to Prolog and trying some exercises. One of them is:
Write a predicate set(InList,OutList)
which takes as input an arbitrary
list, and returns a list in which each
element of the input list appears only
once.
Here is my solution:
member(X,[X|_]).
member(X,[_|T]) :- member(X,T).
set([],[]).
set([H|T],[H|Out]) :-
not(member(H,T)),
set(T,Out).
set([H|T],Out) :-
member(H,T),
set(T,Out).
I'm not allowed to use any of built-in predicates (It would be better even do not use not/1). The problem is, that set/2 gives multiple same solutions. The more repetitions in the input list, the more solutions will result. What am I doing wrong? Thanks in advance.
You are getting multiple solutions due to Prolog's backtracking. Technically, each solution provided is correct, which is why it is being generated. If you want just one solution to be generated, you are going to have to stop backtracking at some point. This is what the Prolog cut is used for. You might find that reading up on that will help you with this problem.
Update: Right. Your member() predicate is evaluating as true in several different ways if the first variable is in multiple positions in the second variable.
I've used the name mymember() for this predicate, so as not to conflict with GNU Prolog's builtin member() predicate. My knowledge base now looks like this:
mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).
not(A) :- \+ call(A).
set([],[]).
set([H|T],[H|Out]) :-
not(mymember(H,T)),
set(T,Out).
set([H|T],Out) :-
mymember(H,T),
set(T,Out).
So, mymember(1, [1, 1, 1]). evaluates as true in three different ways:
| ?- mymember(1, [1, 1, 1]).
true ? a
true
true
no
If you want to have only one answer, you're going to have to use a cut. Changing the first definition of mymember() to this:
mymember(X,[X|_]) :- !.
Solves your problem.
Furthermore, you can avoid not() altogether, if you wish, by defining a notamember() predicate yourself. The choice is yours.
A simpler (and likely faster) solution is to use library predicate sort/2 which remove duplicates in O(n log n). Definitely works in Yap prolog and SWIPL
You are on the right track... Stay pure---it's easy!
Use reified equality predicates =/3 and dif/3 in combination with if_/3, as implemented in Prolog union for A U B U C:
=(X, Y, R) :- X == Y, !, R = true.
=(X, Y, R) :- ?=(X, Y), !, R = false. % syntactically different
=(X, Y, R) :- X \= Y, !, R = false. % semantically different
=(X, Y, R) :- R == true, !, X = Y.
=(X, X, true).
=(X, Y, false) :-
dif(X, Y).
% dif/3 is defined like (=)/3
dif(X, Y, R) :- X == Y, !, R = false.
dif(X, Y, R) :- ?=(X, Y), !, R = true. % syntactically different
dif(X, Y, R) :- X \= Y, !, R = true. % semantically different
dif(X, Y, R) :- R == true, !, X \= Y.
dif(X, Y, true) :- % succeed first!
dif(X, Y).
dif(X, X, false).
if_(C_1, Then_0, Else_0) :-
call(C_1, Truth),
functor(Truth,_,0), % safety check
( Truth == true -> Then_0 ; Truth == false, Else_0 ).
Based on these predicates we build a reified membership predicate list_item_isMember/3. It is semantically equivalent with memberd_truth/3 by #false. We rearrange the argument order so the list is the 1st argument. This enables first-argument indexing which prevents leaving useless choice-points behind as memberd_truth/3 would create.
list_item_isMember([],_,false).
list_item_isMember([X|Xs],E,Truth) :-
if_(E = X, Truth = true, list_item_isMember(Xs,E,Truth)).
list_set([],[]).
list_set([X|Xs],Ys) :-
if_(list_item_isMember(Xs,X), Ys = Ys0, Ys = [X|Ys0]),
list_set(Xs,Ys0).
A simple query shows that all redundant answers have been eliminated and that the goal succeeds without leaving any choice-points behind:
?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [4,3,2,1]. % succeeds deterministically
Edit 2015-04-23
I was inspired by #Ludwig's answer of set/2, which goes like this:
set([],[]).
set([H|T],[H|T1]) :- subtract(T,[H],T2), set(T2,T1).
SWI-Prolog's builtin predicate subtract/3 can be non-monotone, which may restrict its use. list_item_subtracted/3 is a monotone variant of it:
list_item_subtracted([],_,[]).
list_item_subtracted([A|As],E,Bs1) :-
if_(dif(A,E), Bs1 = [A|Bs], Bs = Bs1),
list_item_subtracted(As,E,Bs).
list_setB/2 is like set/2, but is based on list_item_subtracted/3---not subtract/3:
list_setB([],[]).
list_setB([X|Xs1],[X|Ys]) :-
list_item_subtracted(Xs1,X,Xs),
list_setB(Xs,Ys).
The following queries compare list_set/2 and list_setB/2:
?- list_set([1,2,3,4,1,2,3,4,1,2,3,1,2,1], Xs).
Xs = [4,3,2,1]. % succeeds deterministically
?- list_setB([1,2,3,4,1,2,3,4,1,2,3,1,2,1],Xs).
Xs = [1,2,3,4]. % succeeds deterministically
?- list_set(Xs,[a,b]).
Xs = [a,b]
; Xs = [a,b,b]
; Xs = [a,b,b,b]
... % does not terminate universally
?- list_setB(Xs,[a,b]).
Xs = [a,b]
; Xs = [a,b,b]
; Xs = [a,b,b,b]
... % does not terminate universally
I think that a better way to do this would be:
set([], []).
set([H|T], [H|T1]) :- subtract(T, [H], T2), set(T2, T1).
So, for example ?- set([1,4,1,1,3,4],S) give you as output:
S = [1, 4, 3]
Adding my answer to this old thread:
notmember(_,[]).
notmember(X,[H|T]):-X\=H,notmember(X,T).
set([],[]).
set([H|T],S):-set(T,S),member(H,S).
set([H|T],[H|S]):-set(T,S),not(member(H,S)).
The only virtue of this solution is that it uses only those predicates that have been introduced by the point where this exercise appears in the original text.
This works without cut, but it needs more lines and another argument.
If I change the [H2|T2] to S on line three, it will produce multiple results. I don't understand why.
setb([],[],_).
setb([H|T],[H|T2],A) :- not(member(H,A)),setb(T,T2,[H|A]).
setb([H|T],[H2|T2],A) :- member(H,A),setb(T,[H2|T2],A).
setb([H|T],[],A) :- member(H,A),setb(T,[],A).
set(L,S) :- setb(L,S,[]).
You just have to stop the backtracking of Prolog.
enter code here
member(X,[X|_]):- !.
member(X,[_|T]) :- member(X,T).
set([],[]).
set([H|T],[H|Out]) :-
not(member(H,T)),
!,
set(T,Out).
set([H|T],Out) :-
member(H,T),
set(T,Out).
Using the support function mymember of Tim, you can do this if the order of elements in the set isn't important:
mymember(X,[X|_]).
mymember(X,[_|T]) :- mymember(X,T).
mkset([],[]).
mkset([T|C], S) :- mymember(T,C),!, mkset(C,S).
mkset([T|C], S) :- mkset(C,Z), S=[T|Z].
So, for example ?- mkset([1,4,1,1,3,4],S) give you as output:
S = [1, 3, 4]
but, if you want a set with the elements ordered like in the list you can use:
mkset2([],[], _).
mkset2([T|C], S, D) :- mkset2(C,Z,[T|D]), ((mymember(T,D), S=Z,!) ; S=[T|Z]).
mkset(L, S) :- mkset2(L,S,[]).
This solution, with the same input of the previous example, give to you:
S = [1, 4, 3]
This time the elements are in the same order as they appear in the input list.
/* Remove duplicates from a list without accumulator */
our_member(A,[A|Rest]).
our_member(A, [_|Rest]):-
our_member(A, Rest).
remove_dup([],[]):-!.
remove_dup([X|Rest],L):-
our_member(X,Rest),!,
remove_dup(Rest,L).
remove_dup([X|Rest],[X|L]):-
remove_dup(Rest,L).