I want to implement the following problem in prolog:
Given
L1=[1,2,3,4] and L2=[2,3,4]
calling a function named remove_list(L1,L2,L) will remove L2 from L1 .
So L will be [1].
But if the elements of the 2nd list are not in the same order as in L1 or more accurately the 2nd one is not a subset of the first List ,it wont remove anything.
SayL1=[1,2,3,4,5] and L2=[2,3,6] or L2=[2,6] or L2=[4,3,2] will result L=[1,2,3,4,5]
Any help will be highly appreciated.
Thanks in advance
You can build your predicate remove_list/3 using recursivity, which is an useful tool when dealing with lists in Prolog.
remove_list([], _, []).
remove_list([X|Tail], L2, Result):- member(X, L2), !, remove_list(Tail, L2, Result).
remove_list([X|Tail], L2, [X|Result]):- remove_list(Tail, L2, Result).
Consult:
?- remove_list([4,5,1,6,3], [1,4,7], L).
L = [5, 6, 3].
The idea is to copy every element in your original list "L1" to your final list "L", except when that element is member of the second list "L2".
Your base clause is your stop condition, when your original list "L1" is empty, in that case ignoring your list "L2", the result is always the same empty list. (You can't delete nothing from the empty list).
Your second clause, don't copy the element in the head of the list "L1" to the final list "L" if that element in the head is member of the list "L2", also make the recursive call to the predicate with the Tail of your list "L".
And the final clause, copy the element in the head of the list "L1" to the final list "L", and also make the recursive call to the predicate with the Tail of that list "L". We don't need the goal member/2 here, because we used a cut in the previous clause.
EDIT: This answer should only be considered if you want to remove items from the list "L1" contained in the "L2" list, regardless of the order. To remove the subset "L2" from set "L1", please use Lurker's solution or this other solution:
remove_list(L, [], L):- !.
remove_list([X|Tail], [X|Rest], Result):- !, remove_list(Tail, Rest, Result).
remove_list([X|Tail], L2, [X|Result]):- remove_list(Tail, L2, Result).
This new solution considers the order of the elements in list "L2", but not in the strict sense, ie, may be interspersed in the original list "L1", which does not violate the concept of "L2" being a subset of "L1".
[2,4] is a subset of the set [1,2,3,4,5,6], but [2,4,7] is not:
?- remove_list([1,2,3,4,5,6], [2,4], L).
L = [1, 3, 5, 6].
?- remove_list([1,2,3,4,5,6], [4,2], L).
false.
?- remove_list([1,2,3,4,5,6], [2,4,7], L).
false.
Now, given the fact that is desired to obtain the original set rather than the negative response in case that any of the elements from the original set can be removed, then we use an auxiliary predicate:
rm_subset(L1, L2, L):- remove_list(L1, L2, L),!.
rm_subset(L1, L2, L1).
Consult:
?- rm_subset([1,2,3,4,5,6], [4,2], L).
L = [1, 2, 3, 4, 5, 6].
?- rm_subset([1,2,3,4,5,6], [2,4], L).
L = [1, 3, 5, 6].
Another possible solution, using the delete/3 predicate:
remove_elements(L, [H|T], R) :-
delete(L, H, R1),
remove_elements(R1, T, R).
remove_elements(L, [], L).
| ?- remove_elements([4,5,1,6,3], [1,4,7], L).
L = [5,6,3] ? ;
no
| ?-
EDIT I just realized I completely misread the problem. Duh. If you want to maintain order of the "removed" list as stated, then Boris' comment is right on regarding append/3. append(A, B, C) means that if you take A and append B you get C, with element order preserved.
So, to restate the solution as requested:
remove_elements(L1, L2, L) :-
append(A, B, L1),
append(C, L2, A),
append(C, B, L).
In general, if you just want to delete multiple elements in a list from another list you can just use:
subtract(+Set, +Delete, -Result)
where set = unorderd list which can have some duplicated elements and
delete = list of element we want to delete from Set.
Ex:
subtract([1,3,5,6,4,2,3], [1,2,3], R).
R = [5, 6, 4].
Related
I'm new to Prolog, and struggling to do the following:
I need to write a predicate which outputs the number of list elements inside a list of lists, and output the answer itself as a list.
As an example, I would want:
clistoflists([[a,b,c,d],[e,f,g],[h,i][j,k,l]], N).
N = (4,3,2,3)
I am able to write a predicate to count the elements of a simple list:
count_list([],0).
count_list([_|T], C) :- count_list(T, CNT), C is CNT + 1.
I'm just very unsure how to proceed with the more complicated list of lists, and especially providing for the desired output list.
Any guidance would be very welcome. I've been toying with this for far too long.
The simplest solution is to use the predicates length/2 and maplist/3 as follows:
?- maplist(length, [[a,b,c,d],[e,f,g],[h,i],[j,k,l]], L).
L = [4, 3, 2, 3].
Another alternative is to create your own versions of those predicates:
maplen([], []).
maplen([X|Xs], [Y|Ys]) :-
len(X, Y),
maplen(Xs, Ys).
len([], 0).
len([_|Xs], N) :-
len(Xs, M),
N is M + 1.
Example:
?- maplen([[a,b,c,d],[e,f,g],[h,i],[j,k,l]], L).
L = [4, 3, 2, 3].
I'm trying to write a program in Prolog that will take in three lists (all of which are the same length) and return a list of lists.
The list of lists that I am returning is a triple that contains elements from the three lists that are being passed in. The first element of the triple is from the first list passed in, the second element of the triple is from the second list, and the third element of the triple is from the third list passed in.
What I want to have happen is the list of triples that the function is returning to return every single possible combination that you could make from the three lists being passed in.
As of now I have some code that takes the first elements of the three lists and makes a triple out of them, then takes the second element of all the lists and makes a triple out of them, and so on. Here it is below.
listCombos( [], [], [], []).
listCombos( [A|AREST], [B|BREST], [C|CREST], [[A,B,C]|SOLUTION]) :-
listCombos( AREST, BREST, CREST, SOLUTION).
My strategy for getting every combo is taking the first element of the first list and the first element in the second list and then going through each elements in the third list. Once I have done that I will move on the the first element in the first list and the second element in the second list and match those up with each element in the third list. Then after I have went through the second list move onto the first list. Let me know if more clarification on this is needed.
I'm new to Prolog so I don't understand how to turn what I'm planning to do into code. I've tried a few things but haven't been successful and have gotten some error codes I don't understand so it's hard to tell if I'm going in the right direction (I can post some of my attempts if needed). If anyone has some idea of what direction I should go in or some explanation on what I need to do that would be appreciated.
Thank you very much.
Knowing a little Prolog the most obvious solution is something like this:
listCombos(Xs, Ys, Zs, Result) :-
findall([X,Y,Z],
(member(X, Xs), member(Y, Ys), member(Z, Zs)),
Result).
It's advisable to generalize the construct you're looking for, accepting a list of lists to be combined, following the schema from this answer:
combine(Ls,Rs) :- maplist(member,Rs,Ls).
listCombos(A,B,C, SOLUTION) :- findall(R,combine([A,B,C],R),SOLUTION).
We first can solve a related problem: given a list of "heads" Hs and a list of "tails" Ts, construct all lists for all heads H in Hs, and all tails T in Ts in a list. We can do this with a predicate:
merge_all([], _, []).
merge_all([H|Hs], Ts, All) :-
merge_single(Ts, H, All, D),
merge_all(Hs, Ts, D).
merge_single([], _, D, D).
merge_single([T|Ts], H, [[H|T]|Rest], D) :-
merge_single(Ts, H, Rest, D).
For example:
?- merge_all([a, b], [[1, 4], [2, 5]], R).
R = [[a, 1, 4], [a, 2, 5], [b, 1, 4], [b, 2, 5]].
Now we can use this for example to make all cross products with Cs and the "empty set", for example if Cs = [a, b, c], then:
?- merge_all([a, b, c], [[]], RC).
RC = [[a], [b], [c]].
Given we have this result, we can make the cross product of Bs with this result. For example if Bs = [1, 4], then we obtain:
?- merge_all([a, b, c], [[]], RC), merge_all([1, 4], RC, RB).
RC = [[a], [b], [c]],
RB = [[1, a], [1, b], [1, c], [4, a], [4, b], [4, c]].
With the above generating the cross product of three sets should be straightforward, I leave this as an exercise.
The approach by Daniel Lyons is good in that it allows us to easily control the order of combinations in the cross-product of a list of lists, while keeping the order of elements in the combinations the same, of course:
cross( [], [[]] ).
cross( [XS | T], R ):-
cross( T, TC),
findall( [X | Y], ( % or:
member( Y, TC), % member( X, XS)
member( X, XS) % member( Y, TC),
),
R).
It exhibits good modularity and separation of concerns: the order of presentation is independent of the order of generation and the order of selection.
I want to write a reverse/2 function. This is my code and I cannot figure out where the error is.
rev([]).
rev([H|T],X):-rev(T,X),append(T,H,_).
The output:
rev ([1,2,3,4], X).
false.
rev(?List1,?List2) is true when elements of List2 are in reversed order compared to List1
rev(Xs, Ys) :-
rev(Xs, [], Ys, Ys).
rev([], Ys, Ys, []).
rev([X|Xs], Rs, Ys, [_|Bound]) :-
rev(Xs, [X|Rs], Ys, Bound).
Output:
?- rev([1,2,3,4],X).
X = [4, 3, 2, 1].
?- rev([3,4,a,56,b,c],X).
X = [c, b, 56, a, 4, 3].
Explanation of rev/4
On call rev([X|Xs](1), Rs(2), Ys(3), [_|Bound](4))
[X|Xs](1) - List1, the input list in our case (we can either call rev(Z,[3,2,1]).)
Rs(2) - ResultList is a helping list, we start with an empty list and on every recursive call we push (adding as head member) a member from [X|Xs](1).
Ys(3) - List2, the output list (reversed list of List1)
[_|Bound](4) - HelpingList for bounding the length of Ys(3) (for iterating "length of Ys" times).
On every recursion call rev(Xs(5), [X|Rs](6), Ys(3), Bound(7)).,
we push head member X ([X|Xs](1)) to the front of Rs ([X|Rs](6)),
and iterating the next member of Ys (Bound(7),[_|Bound](4)).
The recursion ends when rev([](9), Ys(10), Ys(3), [](12)). is true.
Every [X|Xs](1) (now the list is empty [](9)) member moved in reversed order to Ys(10), we bounded the size of Ys(3) (using [_|Bound](4) and now it's empty [](12)).
Notice that append/3 - append(?List1, ?List2, ?List1AndList2).
was wrong used in your code, append(T,H,_) when H is not a List2 (it's the head member of the list).
Example use of append/2 and append/3:
?- append([[1,2],[3]],X). % append/2 - Concatenate a list of lists.
X = [1, 2, 3].
?- append([4],[5],X). % append/3 - X is the concatenation of List1 and List2
X = [4, 5].
You should not place a space between the functor name rev and the argument list. Usually this gives a syntax error:
Welcome to SWI-Prolog (threaded, 64 bits, version 7.7.1)
SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software.
?- rev ([1,2,3],X).
ERROR: Syntax error: Operator expected
ERROR: rev
ERROR: ** here **
ERROR: ([1,2,3],X) .
Otherwise I think the rev/4 solution aims at a bidirectional solution. If you don't need this, and want to go for an accumulator solution that doesn't leave a choice point, you can try:
reverse(X, Y) :-
reverse2(X, [], Y).
reverse2([], X, X).
reverse2([X|Y], Z, T) :-
reverse2(Y, [X|Z], T).
For example:
createlistoflists([1,2,3,4,5,6,7,8,9], NewLists)
NewLists = [[1,2,3], [4,5,6], [7,8,9].
So basically my first argument is a list, my second argument a new list consisting of lists with the proper length (the proper length being 3). My first idea was to use append of some sort. But I have literally no idea how to do this, any thoughts?
thanks in advance
If you don't mind using the nice facilities Prolog provides you, there's a simple approach;
list_length(Size, List) :- length(List, Size).
split_list(List, SubSize, SubLists) :-
maplist(list_length(SubSize), SubLists),
append(SubLists, List).
And you can query it as:
?- split_list([1,2,3,4,5,6,7,8,9], 3, L).
L = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
It will fail if List is instantiated in such a way that it's length is not a multiple of SubSize.
As pointed out by Will Ness in the comments, the above simple solution has a flaw: the maplist(list_length(SubSize), SubList) will continue to query and find longer and longer sets of sublists, unconstrained. Thus, on retry, the query above will not terminate.
The temptation would be to use a cut like so:
split_list(List, SubSize, SubLists) :-
maplist(list_length(SubSize), SubLists), !,
append(SubLists, List).
The cut here assumes you just want to get a single answer as if you were writing an imperative function.
A better approach is to try to constrain, in a logical way, the SubList argument to maplist. A simple approach would be to ensure that the length of SubList doesn't exceed the length of List since, logically, it should never be greater. Adding in this constraint:
list_length(Size, List) :- length(List, Size).
not_longer_than([], []).
not_longer_than([], [_|_]).
not_longer_than([_|X], [_|Y]) :-
not_longer_than(X, Y).
split_list(List, SubSize, SubLists) :-
not_longer_than(SubLists, List),
maplist(list_length(SubSize), SubLists),
append(SubLists, List).
Then the query terminates without losing generality of the solution:
?- split_list([1,2,3,4,5,6,7,8,9], 3, L).
L = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] ;
false.
?-
One could be more precise in the implementation of not_longer_than/2 and have it use the SubSize as a multiple. That would be more efficient but not required to get termination.
not_longer_than_multiple(L1, Mult, L2) :-
not_longer_than_multiple(L1, Mult, Mult, L2).
not_longer_than_multiple([], _, _, []).
not_longer_than_multiple([], _, _, [_|_]).
not_longer_than_multiple([_|Xs], Mult, 1, [_|Ys]) :-
not_longer_than_multiple(Xs, Mult, Mult, Ys).
not_longer_than_multiple(Xs, Mult, C, [_|Ys]) :-
C #> 1,
C1 #= C - 1,
not_longer_than_multiple(Xs, Mult, C1, Ys).
Or something along those lines...
But then, if we're going to go through all that non-sense to cover the sins of this use of maplist, then perhaps hitting the problem head-on makes the cleanest solution:
split_list(List, SubSize, SubLists) :-
split_list(List, SubSize, SubSize, SubLists).
split_list([], _, _, []).
split_list([X|Xs], SubList, 1, [[X]|S]) :-
split_list(Xs, SubList, SubList, S).
split_list([X|Xs], SubSize, C, [[X|T]|S]) :-
C #> 1,
C1 #= C - 1,
split_list(Xs, SubSize, C1, [T|S]).
I am trying to write prolog code that will delete all punctuation (.,!? etc) from all lists in a list of lists. This is what I have so far:
delete_punctuation(_,[],_).
delete_punctuation(Character,[List|Tail],Resultlist) :-
delete(List,Character,NewList),
delete_punctuation(Character,Tail,[NewList|Resultlist]).
whereas 'Character' will be 33 for ! or 46 for . and so on since I will be using this only on lists of character codes. (I know, that the function would actually work for other elements that I would like to delete from the lists too.)
The results I receive when asking:
delete_punctuation(33,[[45,33,6],[4,55,33]],X).
is just
|: true.
However, I want it to be:
|: X = [[45,6],[4,55]].
What do I need to improve?
For this problem, I'd tackle it by addressing the two sub-problems separately, namely:
Filter/exclude a character code from a single list;
Applying the solution to the above to a list of lists of character codes.
To this end, I'd approach it like this:
exclude2(_, [], []).
exclude2(Code, [Code|Xs], Ys) :-
!, % ignore the next clause if codes match
exclude2(Code, Xs, Ys).
exclude2(Code, [X|Xs], [X|Ys]) :-
% else, Code != X here
exclude2(Code, Xs, Ys).
Note that some implementations like SWI-Prolog provide exclude/3 as a built-in, so you mightn't actually need to define it yourself.
Now, to apply the above predicate to a list of lists:
delete_punctuation(_, [], []).
delete_punctuation(Code, [L|Ls], [NewL|NewLs]) :-
exclude(Code, L, NewL),
delete_punctuation(Code, Ls, NewLs).
However, again, depending on the implementation, a built-in like maplist/3 could be used to achieve the same effect without having to define a new predicate:
?- maplist(exclude2(33), [[45,33,6],[4,55,33]], X).
X = [[45, 6], [4, 55]] ;
false.
n.b. if you want to use all SWI built-ins, exclude/3 requires the test to be a goal, like so:
?- maplist(exclude(==(33)), [[45,33,6],[4,55,33]], X).
X = [[45, 6], [4, 55]] ;
false.
For a more general approach, you could even add all the codes you want to exclude (such as any and all punctuation character codes) to a list to use as the filter:
excludeAll(_, [], []).
excludeAll(Codes, [Code|Xs], Ys) :-
member(Code, Codes),
!,
excludeAll(Codes, Xs, Ys).
excludeAll(Codes, [X|Xs], [X|Ys]) :-
excludeAll(Codes, Xs, Ys).
Then you can add a list with all the codes to delete:
?- maplist(excludeAll([33,63]), [[45,33,6],[4,55,33,63]], X).
X = [[45, 6], [4, 55]] ;
false.