Related
I have a List and I am trying to add to it elements from another list that are not already present in the first List.
So if I had 2 Lists :
[a, b, 3, c]
[2, a, b, 4]
The output would be:
[a, b, 3, c, 2, 4]
I am able to get it in reversed order but not in the correct one, here is what I am trying to do:
add_to_list(L, [], L).
add_to_list(List, [H|T], [H|Res]) :-
\+ member(H, List),
add_to_list(List, T, Res).
add_to_list(List, [H|T], Res):-
add_to_list(List, T, Res).
And when I do the method with the 2 Lists mentioned above the output I get is:
[2, 4, a, b, 3, c]
I am aware that my ending clause is adding the L to the end of the result I get, which is why the order is a mess but how can I do it the correct way?
Well the problem here is that you should first move to the end of the first list before concatenating data.
We can still use the code you have written, but alter it slightly like:
atl(_, [], []).
atl(List, [H|T], R) :-
( member(H, List)
-> R = Res
; R = [H|Res]
),
atl(List, T, Res).
We here basically made three changes: (a) we renamed addToList/3 to atl/3; we changed L to [] in the first line; and (c) we used an if-then-else to prevent that the third clause gets triggered even if H is not a member of List (this was a semantical error in your code).
Now we will obtain for the given input as output:
?- atl([a, b, 3, c] , [2, a, b, 4], R).
R = [2, 4] ;
false.
So now we can write an addToList/3 in terms of atl/3: we first generate the list of items to append, and next we use append/3 to append these at the end of the list:
addToList(A, B, L) :-
atl(A, B, R),
append(A, R, L).
I'm doing random problems for practice and I keep running into the same little issue with lists. For some reason I keep getting a pipe symbol at the end of the List when I'm inserting/appending/modifying the List. Example of one such code that does that to me:
insertBetween([], E, []).
insertBetween([X], E, X).
insertBetween([X|Xs], E, [X, E | Zs]):- insertBetween(Xs, E, Zs).
I try the following command:
insertBetween([1,2,3], 0, Res).
which return as : Res = [1, 0, 2, 0|3].
Why Am I getting this answer when I'm expected to get Res = [1,0,2,0,3]? Am I doing the base cases wrong? For the last few weeks, I have come to dislike this Program lang... lol
In addition to #false's answer even if you write:
insertBetween([X], E, [X]). instead of insertBetween([X], E, X).
you get:
?- insertBetween([1,2,3], 0, Res).
Res = [1, 0, 2, 0, 3] ;
Res = [1, 0, 2, 0, 3, 0].
That because the second and third rules/clauses can both succeed giving different results ,so you need to make third clause differ from second like:
insertBetween([], _, []).
insertBetween([X], _, [X]).
insertBetween([X,Y|Xs], E, [X, E | Zs]):- insertBetween([Y|Xs], E, Zs).
This gives:
?- insertBetween([1,2,3], 0, Res).
Res = [1, 0, 2, 0, 3] ;
false.
Note that the third clause works if the list has at least two elements but when it has only one then second clause succeeds. Also when you have for example: insertBetween([], E, []). where E is not used it is better replacing it with insertBetween([], _, []).
insertBetween([X], E, X).
^ ^
So X is an element of a list and a list.
For the following query (and the predicates defined in the following) I get an unexpected answer:
?- rep([1,2,3], 3, [2,3,4], L).
L = [1, 2, 2, 3, 4] ;
L = [1, 2, 3]. % unexpected answer
The first result is the one I want. The second one I do not want...
How can I prevent the second one? Probably by adding ! somewhere?
concat([], L, L).
concat([H|T], L, [H|Res]) :-
concat(T, L, Res).
repl([], _, _, []).
repl([Val|T], Val, Repl, Res) :-
repl(T, Val, Repl, Temp),
concat(Repl, Temp, Res).
repl([H|T], Val, Repl, [H|Res]) :-
repl(T, Val, Repl, Res).
To allow for multiple matches per list, use meta-predicate maplist/3 and proceed like this:
item_replacement_item_mapped(E, Es, E, Es).
item_replacement_item_mapped(X, _, E, [E]) :-
dif(X, E).
repl(Es0,X,Xs,Es) :-
maplist(item_replacement_item_mapped(X,Xs), Es0, Ess1),
append(Ess1, Es).
Sample queries:
?- repl([1,2,3], 3, [2,3,4], L).
L = [1,2,2,3,4]
; false.
?- repl([x,y,x,y,x], x, [x,y,x], L).
L = [x,y,x,y,x,y,x,y,x,y,x]
; false.
As #repeat has already nicely shown, you should use the constraint dif/2 to describe that two terms are different. This avoids the unexpected and wrong second solution.
In addition, as always when describing lists, also consider using dcg notation: You can use the nonterminal list//1 do declaratively describe a list in such a way that it can be easily and efficiently spliced into other lists at specific positions.
Consider:
replacement([], _, _) --> [].
replacement([L|Ls], L, Rs) -->
list(Rs),
replacement(Ls, L, Rs).
replacement([L|Ls], R, Rs) --> [L],
{ dif(L, R) },
replacement(Ls, R, Rs).
list([]) --> [].
list([L|Ls]) --> [L], list(Ls).
We use the interface predicate phrase/2 to use the DCG. For example:
?- phrase(replacement([1,2,3], 3, [2,3,4]), Ls).
Ls = [1, 2, 2, 3, 4] ;
false.
It is a true relation that works in all directions. It can answer quite general questions, such as: Which item has been replaced by another list? Example:
?- phrase(replacement([1,2,3], E, [2,3,4]), [1,2,2,3,4]).
E = 3 ;
false.
edit
this is getting hairy, and my answer didn't accounted precisely for request... so let's see your code with minimal change:
concat([], L, L).
concat([H|T], L, [H|Res]) :-
concat(T, L, Res).
repl([], _, _, []).
repl([Val|T], Val, Repl, Res) :- !, % as noted by #repeat, better to commit early...
repl(T, Val, Repl, Temp),
concat(Repl, Temp, Res). % !.
repl([H|T], Val, Repl, [H|Res]) :-
repl(T, Val, Repl, Res).
the cut simply commits the second clause...
resume old answer
your concat/3 is the same as the well known append/3, so consider this approach
repl(ListOrig, Element, Replace, ListUpdated) :-
append(H, [Element|T], ListOrig),
append(H, Replace, Temp),
append(Temp, T, ListUpdated).
?- repl([1, 2, 3], 3, [2, 3, 4], L).
L = [1, 2, 2, 3, 4] ;
false.
edit
as requested by comments, this extension handles a list of Element to match for change, with simple pattern matching (note: add before the previous clause)
repl(ListOrig, [], _Replace, ListOrig).
repl(ListOrig, [E|Es], Replace, ListUpdated) :-
repl(ListOrig, E, Replace, Temp),
repl(Temp, Es, Replace, ListUpdated).
test
?- repl([1,2,3],[2,3],[x,y,z],R).
R = [1, x, y, z, x, y, z] ;
false.
edit
I didn't noticed that if Element is not found it should not fail...
a last 'catchall' clause could handle this case:
repl(ListOrig, _Element, _Replace, ListOrig).
or better, extend the original like
repl(ListOrig, Element, Replace, ListUpdated) :-
( append(H, [Element|T], ListOrig)
-> append(H, Replace, Temp),
append(Temp, T, ListUpdated)
; ListOrig = ListUpdated
).
I need to merge two lists in Prolog. On input should be predicate merge/3.
Should work like this:
?- merge([6,4,b,8], [5,b,s,6], X).
X = [6, 4, b, 8, 5, s].
What I have tried:
%rules
merge(A, B, X):-
merge(A, B, B, X).
merge([], X, _, X).
merge([Head|L1], [Head|L2], Tmp, [Head|X]) :-
merge(L1, L2, Tmp, X),
!.
merge(L1, [_|L2], Tmp, X) :-
merge(L1, L2, Tmp, X),
!.
merge([A|L1], [], Tmp, [A|X]) :-
merge(L1, Tmp, Tmp, X),
!.
What I get:
?- merge([1,2,a,3], [5,d,a,1], X).
X = [1, 2, a, 3, 5, d, a, 1].
What I expect:
?- merge([1,2,a,3], [5,d,a,1], X).
X = [1, 2, a, 3, 5, d].
If the order of the elements does not somehow depend on the order of the two input lists, this is an idiomatic Prolog solution:
?- append([6,4,b,8], [5,b,s,6], A), sort(A, B).
A = [6, 4, b, 8, 5, b, s, 6],
B = [4, 5, 6, 8, b, s].
If the order is important, you need to explain how exactly.
And some comments on the code you show. The names that you have chosen for your predicates: both "join" and "merge" have well-established meanings different from what you seem to be attempting to achieve ("join" as in relational databases, "merge" as in "merge two ordered lists"). What you are doing is rather a "union" (and by the way, click on this link and read the code!).
Also, it is almost always a mistake (not an error, but a mistake) to have a cut as the last subgoal of a clause body. Having multiple clauses to a predicate that are not obviously mutually exclusive (as the last 3 of the 4 clauses to your merge/4) is commonly a design flaw (not a mistake).
This can be done by rewriting built-in predicates ! e.g :
my_append([], R, R) .
my_append([H|T], R1, [H|R2]) :-
my_append(T, R1, R2).
my_member(H, [H|_]).
my_member(H, [_|T]) :-
my_member(H, T).
So, I can say that merging L with an empty list gives this list L
merge(L, [], L).
Now, to merge two lists, I look at the first element of the second list.
If it is in the first list, I ignore it and I merge the first list, with the rest of the second.
If not, I add this first element at the end of the first list and I merge the new first list with the rest of the second.
I must say that it's not very efficient !!
merge(L, [H| T], R) :-
( my_member(H, L)
-> merge(L, T, R)
; my_append(L, [H], L1),
merge(L1, T, R)).
I have a list of lists, and I need to find the longest one of them. If there are more than one with the same length it's the same which it returns. Thanks.
Here is a general predicate that scans a list to find a single member defined by a given goal.
select_element(Goal, [Head | Tail], Selected) :-
select_element(Goal, Tail, Head, Selected).
select_element(_Goal, [], Selected, Selected).
select_element(Goal, [Head | Tail], Current, FinalSelected) :-
call(Goal, Head, Current, Selected),
select_element(Goal, Tail, Selected, FinalSelected).
Lets say you define a predicate
get_bigger_number(N1, N2, N) :-
N is max(N1, N2).
Now you can execute:
?- select_element(get_bigger_number, [5, 1, -2, 10, 3.2, 0], Selected).
Selected = 10
So all you need to do now is define a predicate get_longer_list(L1, L2, L),
and use it instead of get_bigger_number/3.
Of course, using a general predicate like select_element/3 might not be very efficient. For example, you should try to avoid calculating the length of the same list several times, because this calculation is slow in Prolog (at least if implemented in Prolog in the standard way).
Please consider my aproach.
longest([L], L) :-
!.
longest([H|T], H) :-
length(H, N),
longest(T, X),
length(X, M),
N > M,
!.
longest([H|T], X) :-
longest(T, X),
!.
Then you can consult it:
?- longest([[1]], N).
N = [1] ;
?- longest([[1],[2]], N).
N = [2] .
?- longest([[1],[2], [3,3,3], [2]], N).
N = [3, 3, 3] ;
?- longest([[1],[2], [3,3,3], [2]], N).
N = [3, 3, 3].
?- longest([[1],[2], [3,3,3], [2], [4,4,4,4]], N).
N = [4, 4, 4, 4] .
?- longest([[1],[2], [3,3,3], [2], [4,4,4,4]], N).
N = [4, 4, 4, 4] ;
Greets!
We define longest/2 based on meta-predicate max_of_by/3 used in tandem with length/2:
longest(Xss,Ys) :-
max_of_by(Ys,Xss,length).
Sample queries:
?- longest([[1],[2]],Xs). % we expect multiple solutions
Xs = [1]
; Xs = [2]. % we _get_ multiple solutions
?- longest([[2,1,3],[7,5],[1,8,2,3,1],[2,7,1,4]],Xs).
Xs = [1,8,2,3,1]. % succeeds deterministically
Here is another approach that is efficient and easy to understand. The idea is to find the lengths of all lists in the list, use max_list to get the length of the longest list, and then find a list that is that long. This has the benefit that it will return all lists of the longest length.
lengths([],[]).
lengths([H|T], [LH|LengthsT]) :-
length(H, LH),
lengths(T, LengthsT).
lengthLongest(ListOfLists, Max) :-
lengths(ListOfLists, Lengths),
max_list(Lengths, Max).
longestList(ListOfLists, Longest) :-
lengthLongest(ListOfLists, Len),
member(Longest, ListOfLists),
length(Longest, Len).
% Correct again.
longest(LL,LX) :-
findmax(Len,(append(_,[L|_],LL),length(L,Len)),MaxLen),
append(_,[LX|_],LL),
length(LX,MaxLen).
findmax(V,P,Max) :-
findall(V,P,L),
max(L,Max).
max([N],N) :- !.
max([N|R],Max) :-
max(R,Max2),
max3(N,Max2,Max).
max3(N,Max2,N) :- N > Max2,!.
max3(N,Max2,Max2).
To have the length of longest list:
%sample: longest([[2,1,3],[7,5],[1,8,2,3,1],[2,7,1,4]],L,LEN).
longest([L], L, _) :-
!.
longest([H|T], H, _) :-
length(H, N),
longest(T, X, N),
length(X, M),
N > M,
!.
longest([_|T], X, LEN) :-
length(X, LEN),
longest(T, X, LEN),
!.