Related
I'm having a hard time coming up with an efficient clause set for the following problem: given a list X find its maximum prefix consisting of same elements along with the remaining suffix. That is:
| ?- trim([a,a,a,b,b,c], [a,a,a], [b,b,c]).
yes
| ?- trim([a,a,a,a,b,b,c,c], X, Y).
X = [a,a,a,a],
Y = [b,b,c,c]
Here is what I have so far:
same([]).
same([_]).
same([X,X|T]) :- same([X|T]).
trim([], [], []).
trim(L, L, []) :- same(L).
trim(L, [A|B], [C|D]) :- append([A|B], [C|D], L), A \= C, same([A|B]).
The append part doesn't seem very efficient though. Is there a simple, iterative way to accomplish this?
Thinking about this problem from the start, we know we want the trivial case to be true:
trim([], [], []).
Then we want the longest repeated element prefix case:
trim([X], [X], []). % Trivial case
trim([X,Y|T], [X], [Y|T]) :- % Non-repeating element, ends recursion
dif(X, Y).
trim([X,X|T], [X|Xs], S) :- % Repeating element, recursive case
trim([X|T], Xs, S).
I have a list of facts like this:
set(z,a).
set(z,3).
set(z,k).
set(z,10).
set(z,z).
set(z,1).
And I need to sort them like this:
?- sorted(z, List).
List = [1, 3, 10, a, k, z].
How this doesn't seem too bad. But the problem is that I'm required to use repeat to do it.
I have no idea how to do it using repeat. Here's two simple ways I found out without repeat:
sorted(Set, Z) :-
setof(X, set(Set,X), Z).
sorted2(Set,Sorted) :-
findall(X, set(Set,X), List),
sort(List, Sorted).
Here's something I tried doing on my own:
member(X, [Y|T]) :- X = Y; member(X, T).
smallest(Set,A) :-
findall(X, set(Set,X), Xs),
sort(Xs, [A|_]).
sorted(Set, List) :-
List is [],
repeat,
smallest(Set, CurrentSmallest),
not(member(CurrentSmallest, List)) ->
append(List, CurrentSmallest, List), % don't know how to keep adding to this list
length(List,ListLength),
aggregate_all(count, set(Set,_), FactCount),
ListLength = FactCount.
Idea here being that I start with any empty list. Then we start repeating and taking smaller elements one by one. If we haven't already added the element into the answer we add it to the list. Otherwise we don't. Once the list is the same length as our fact base we succeed and stop the repeat.
But adding to the same list clearly doesn't work like that. Also right now it doesn't work when we have duplicate elements in the set. This is very confusing.
EDIT:
I tried changing it a bit and using asserts for the List like Daniel Lyons said. At the moment it still doesn't work. One reason for that that I suspect of is that it keeps taking the same CurrentSmallest on every repeat. I need it to stop doing that, but I have no idea how.
:- dynamic list/1.
assert(list([])).
member(X, [Y|T]) :- X = Y; member(X, T).
smallest(Set,A) :-
findall(X, set(Set,X), Xs),
sort(Xs, [A|_]).
sorted(Set, Answer) :-
repeat,
smallest(Set, CurrentSmallest),
list(List),
not(member(CurrentSmallest, List)) ->
(
append(List, CurrentSmallest, List2),
retract(list(List)),
assert(list(List2))
),
list(Answer),
length(Answer,ListLength),
aggregate_all(count, set(Set,_), FactCount),
ListLength = FactCount.
I have the list [r,s,2,t,3,u,v] and I need to make a list that will look like [r,5,s,2,t,3,u,5,v,5]. The rule is: for every non integer that is not followed by an integer, a 5 will be added after that element.
I am new to Prolog and this is my code so far:
insertInL([],[]).
insertInL([F,S|Tail], [F,X|Rest]) :-
integer(S), X = S, insertInL(Tail, Rest).
I know there should be one case where S is not an integer but I don't know hot to treat it.
Edit:
I renewed my code:
insertInL([],[]).
insertInL([F,S|T1], [F,S|T2]) :-
integer(S), insertInL(T1, T2).
insertInL([F,S|T1], [F,1|T2]) :-
\+ integer(S), insertInL([S|T1], T2).
Now it does fine unless I have a non integer as last element.
Edit2:
Now it works properly.
insertInL([],[]).
insertInL([F],[F,1]) :-
\+ integer(F).
insertInL([F,S|T1], [F,S|T2]) :-
integer(S), insertInL(T1, T2),!.
insertInL([F,S|T1], [F,1|T2]) :-
\+ integer(S), insertInL([S|T1], T2).
Here's how you could do it while preserving logical-purity!
Based on if_/3 and integer_t/2 we define:
list_fived([], []).
list_fived([X|Xs], [X|Ys]) :-
if_(integer_t(X),
list_fived(Xs, Ys),
past_nonint(Xs, Ys)).
past_nonint([], [5]).
past_nonint([X|Xs], Ys0) :-
if_(integer_t(X),
(Ys0 = [X|Ys], list_fived(Xs, Ys)),
(Ys0 = [5|Ys], list_fived([X|Xs], Ys))).
Sample query using SICStus Prolog 4.3.2:
| ?- list_fived([r,s,2,t,3,u,v], Xs).
Xs = [r,5,s,2,t,3,u,5,v,5] ? ; % expected result as given by the OP
no
Ex: If I have been given two list [1,4,3,2,5,6] and [1,2,3] the final list should be [4,5,6].
i.e., Del([1,4,3,2,5,6], [1,2,3], Result).
----should output Result=[4,5,6].
I have tried something like this:
delete1(A, [A|B], B).
delete1(A, [B, C|D], [B|E]) :- delete1(A, [C|D], E).
But the output I'm getting is by deleting the element being passed as an parameter and not a list.
Output:
delete1(a,[a,b,c,d],Res).
(0) Call: delete1(a,[a,b,c,d],_h210) ?
(0) Exit: delete1(a,[a,b,c,d],[b,c,d]) ?
Res = [b,c,d]
Can anyone please help me how to go about this ?
Pure and simple: Use meta-predicate tfilter/3 in tandem with list_nonmember_t/3!
Like we did with memberd_t/3, we define list_nonmember_t/3 based on if_/3 and (=)/3:
list_nonmember_t([],_,true).
list_nonmember_t([E|Es],X,T) :-
if_(E=X, T=false, list_nonmember_t(Es,X,T)).
Let's put it together!
?- tfilter(list_nonmember_t([1,2,3]), [1,4,3,2,5,6], Xs).
Xs = [4,5,6]. % succeeds deterministically
del1([], _, []).
del1([A|L], B, R) :- member(A, B), del1(L, B, R).
del1([A|L], B, [A|R]) :- not(member(A,B)), del1(L, B, R).
So your delete1 is a good start, it allows to delete single element from the list, but it is not handling all of the cases. For example the lists that are not containing the element to be deleted.
So the right one would be :
delete1(_, [], []).
delete1(A, [A|B], B).
delete1(A, [C|B], [C|E]) :- delete1(A, B, E).
and then using this, you can define your del1 by applying delete1 recursively on the whole list:
del1([], _, []).
del1(L, [], L).
del1(L, [H|T], R) :-
delete1(H, L, R1),
del1(R1, T, R).
And of course you can use builtin list predicates as stated in the other answer.
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([],[]).