I am attempting to write a program that takes two lists as input and checks for proper subset. I started with:
proper([A],[]).
proper([],[A]).
proper([A|T1],[A|T2]) :- proper(T1,T2).
This works perfectly fine for inputs in the exact same order. for instance:
?- proper([a,b,c],[a,b,c,d]).
Yes
But doesn't for inputs such as:
?- proper([a,b,c],[b,d,a,c]).
No
After looking through the site I found this previously asked question:
Subset function in prolog
Which lead me to modify my code as such:
proper([A],[]).
proper([],[A]).
proper([A|T1],[A|T2) :- member(A,T2), proper(T1,T2).
proper([H1|T1], [H2|T2]) :- \+ member(H1, T2).
This works fine for subsets but not for proper subsets. Which I think my problem is arising from my understanding of how the second clause of proper/4 works. Any and all help is greatly appreciated.
Edit:
Realized I was trying to determine if the first list was a proper subset of the second and the second was a proper subset of the first. Cleaned up the code to be more precise.
proper([],_).
proper([A|T1],[A|T2) :- member(A,T2), proper(T1,T2).
proper([H1|T1], [H2|T2]) :- \+ member(H1, T2).
A short explanation: subseq0(X, Y) checks whether Y is a subset subsequence of X, while subseq1(X, Y) checks whether Y is a proper subset subsequence of X.
Used to test if a specified list contains all elements of another list, or to generate all sublists of a given list. The definition of this Prolog library predicate is: subset([],[]). subset([X|L],[X|S]) :- subset(L,S).
If I understand correctly, the first two declarations in your last attempt would mean that, both a list with 1 element is a proper subset of an empty list (false), and that an empty list is a proper subset of a list with one element (true); the first one should be problematic, because proper([1], [])
will succeed as well as proper([],[1])
, but the proper subset relation is asymmetric.
I believe the reason that your second attempt doesn't filter out identical subsets is that you have no declaration that requires A to be smaller than B.
Here are some possible solutions I came up with. I use smaller_set/2
a couple times for increased clarity and concision.
smaller_set(A, B) :-
length(A, LA),
length(B, LB),
LA < LB.
def_proper_subset/2
tries to capture the standard definition of a subset.
def_proper_subset(A, B) :-
smaller_set(A, B), % if A < B then there's some _e in B that isn't in A.
forall(member(_e, A), member(_e, B)).
An example with recursive definition, based on removeing each matching element of A and B. It assures that A < B by only succeeding if A runs out of elements before B.
rec_proper_subset1([], [_|_]).
rec_proper_subset1([_e|A], B) :-
select(_e, B, C), % C is B with _e removed. Only true if _e is in B.
rec_proper_subset1(A, C).
This one uses an auxiliarry predicate to check membership once the main predicate has already assured that A < B.
rec_proper_subset2(A, B) :-
smaller_set(A, B),
rec_proper_subset2_(A, B).
rec_proper_subset2_([], _).
rec_proper_subset2_([_e|A], B) :-
member(_e, B),
rec_proper_subset2_(A, B).
Edit:
list_to_set/2
, sort/2
, or something similar if you want to be sure that your lists don't have any duplicate elements. But these kinds of solutions will also work to find sub lists.def_proper_subset/2
is a kind of crappy solution, because it will only work to check that A is a subset of B but can't generate a subset of B in A. The other two are able to thought.(I screwed up and forgot to include the ground definition for rec_proper_subset2/2
, but I have now fixed it).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With