Prolog - how to check if a list includes certain elements? - list

I am trying out Prolog for the first time and am having a little difficulty using lists.
Say I have a list of elements. I want to check that the list has the following elements:
All of: A1, A2, A3, A4, A5
One of: B1, B2, B3, B4
Two of: C1, C2, C3, C4, C5, C6
For example, [A1, A2, B2, C1, A3, A4, C4, A5] meets the requirements and [A2, A1, C1, B1, A3, A4] does not.
How would I got about writing something that returns Yes/True if a list meets the requirements and No/False otherwise? Similarly, how about writing something that returns the missing values from the list needed to meet the requirements?

You asked a lot of questions! Let me get you started with some predicates that solve most of your requirements.
First let's tackle the case of checking that all entries of one list are also in the other list:
subset([ ],_).
subset([H|T],List) :-
member(H,List),
subset(T,List).
This simple recursion makes use of the familiar member/2 predicate to verify each entry in the list specified by the first argument of subset/2 is also in the list specified by the second argument. [For simplicity I've assumed that entries of these list are distinct. A more elaborate version would be needed if we wanted to verify multiple instances of an entry of the first list are matched to at least that many instances in the second list.]
Okay, how about a check that (at least) one of a first list belongs also to the second list? Obviously this is a different predicate than the one above. Instead of all items in the first list, the goal is to be satisfied if there exists any one item in the first list that belongs to the second list.
intersects([H|_],List) :-
member(H,List),
!.
intersects([_|T],List) :-
intersects(T,List).
This recursion fails if it reaches an empty list for the first argument, but succeeds if at any point before that a member of the first list is found that belongs to the second list. [This predicate would work fine even if multiple instances of an item occur in either list. However we'd need to refine the logic if we wanted check exactly one item of the first list belongs to the second list, and that would entail worrying about whether multiple instances are consistent with or counter to the exact count of one.]
What if we want to generalize this check, to verify (at least) N items of the first list are in the second one? The resulting predicate will require a third argument:
intersectsAtLeast(_,_,N) :- N <= 0, !.
intersectsAtLeast([H|T],L,N) :-
member(H,L),
!,
M is N-1,
intersectsAtLeast(T,L,M).
intersectsAtLeast([_|T],L,N) :-
intersectsAtLeast(T,L,N).
This recursion works through the list, decrementing the third argument by one each time an item on the first list turns out to be in the second list as well, and succeeding once the count is reduced to 0 (or less). [Again the code here needs more work if the lists can have repetitions.]
Finally you ask about writing something that "returns the missing values" need to meet requirements. This is not well defined in the case of checking for one or more items on both lists, since a "missing value" might be any one of a number of possible items. In the special case where we asked for all the items on the first list to belong to the second list, the "missing values" can be determined (if any).
missingValues([ ],_,[ ]).
missingValues([H|T],L,K) :-
member(H,L),
!,
missingValues(T,L,K).
missingValues([H|T],L,[H|K]) :-
missingValues(T,L,K).
Here the recursion "moves" items from the input first list to the output "missing items" third list if and only if they do not appear in the second list.
One final note about your questions concerns notation. In Prolog variables are identifiers that start with a capital letter or an underscore, so the use of A1, A2, etc. as items on the list is heading for trouble if those are treated as "unknowns" rather than (as I assume you meant) distinct atoms (constants). Switching to lowercase letters would solve that.

Related

How to make predicate repeat N number of times in Prolog?

I have a number of lists containing letters and I have written a predicate that checks whether or not there are duplicates present in one of these given lists:
noDuplicates([]).
noDuplicates([H|T]):-
not(member(H, T)),
noDuplicates(T).
I have 10 lists and I want to know if there are no duplicates in any of them, so I made them into sublists of one big list, something like:
[[A,B,C], [C,A,D], [E,F,G]...]]
(So there can be duplicates in the big list, but not the individual sublists).
I get that I have to do the duplicates test 10 times; once for every sublist, but how do I write this in Prolog? I could probably write it down 10 times, but my guess is I can use recursion to make prolog repeat itself until all sublists have been checked.
So basically: I want this predicate to repeat itself N times, until N is 10. I'm really struggling with it though. Does anyone have any idea on what to do?
Let us generalize the question as follows:
You have a predicate p/1 that expresses what you want for a single list.
Thus, to lift this definition to a list of such lists, you can define a predicates ps/1 as follows:
ps([]).
ps([L|Ls]) :-
p(L),
ps(Ls).
Every time you see this pattern, you can use maplist/2. That is, the above is equivalent to:
ps(Ls) :- maplist(p, Ls).
The goal maplist(p, Ls) is true iff p holds for each element L of Ls.
Note that it will limit your understanding of Prolog if you think in terms of "looping" and "repeating". These are imperative notions and only make sense when the list is already fully instantiated. However, from Prolog, we expect more than that: We expect a full-fledged relation to also generate lists for which the relation holds. And in such cases, there is nothing to "repeat" yet: We start from nothing, and ask Prolog what solutions there are in general.
Thus, think in terms of describing when the relation ps/1 holds for lists of lists:
It holds for the empty list [].
It holds for the list [L|Ls] if our initial predicate (p/1) holds for L, and ps/1 holds for the remaining list Ls.
This declarative reading is applicable in all directions, no matter how many list elements are already instantiated, if any. It works for 10 lists just as well as for zero and 50.

Prolog - Modifying and returning list

I want to define predicate which takes a list, adds an element to the list, let's say the number "1", and then returns the list.
I've found out I can add elements to a list using append/3, but I want to use in inside another predicate, thus why I want it to return "my modified list".
My object-oriented mindset tells me to ask the interpreter something like: ?-append(X,5,X). , so that it takes the list X, adds 5 to it, and returns "the new X", but I know that's not how unification works, so my mind is kinda in a glitch.
Can anyone please try to explain how something like this could be achievable?
You are already very close to the solution, so I only rephrase what you are beginning to sense already:
First, you cannot modify a list in pure Prolog.
Instead, you should think in terms of relations between entities. In your case, think in terms of relations between lists.
So, "adding the number 1" to a list is a relation between two lists, which could look like this:
list_with_one(Ls, [1|Ls]).
Note that this works in all directions! You can use it to:
generate answers
test particular cases
"reverse" the direction etc.
So, all you need to do in your case is to think in terms of relations between lists: One without an element, and how this relates to a different list with the element.
Obviously, these two lists will be indicated by different variables and different arguments.
Note in particular that append(X, 5, X) cannot hold: First of all, append/3 is meant to be a relation between lists, and 5 is not a list. Second, assuming you wrote for example append(Xs, [5], Xs), then this would be true if there where a list Xs such that if the element 5 were appended to Xs, the resulting list would again be Xs. Good luck finding such a list... Note also the naming convention to denote lists by letting the variable name end with an s.
It is also falls a bit short to blame this on your "object-oriented mindset", since you can have object oriented programming in Prolog too.
Although lists in Prolog cannot be modified, it is possible to add elements to the end of a list with an unspecified length. In this way, items can be "appended" to a list without creating another list:
:- initialization(main).
append_to_list(List,Item) :-
append_to_list(List,Item,0).
append_to_list(List,Item,Index) :-
% using SWI-Prolog's nth0 predicate
(nth0(Index,List,Check_Item),
var(Check_Item),
nth0(Index,List,Item));
(Next_Index is Index+1,
append_to_list(List,Item,Next_Index)).
main :-
A = [1,2,3|_],
append_to_list(A,4),
append_to_list(A,7),
writeln(A).
In this example, A becomes [1,2,3,4,7|_].

Reverse the last two elements of a list in Prolog

I am trying to write the following predicate in Prolog while not making use of append/3:
lastTwoReversed(List, ListOf2)
which succeeds exactly when ListOf2 contains the last and the second-to-last elements of List in that order (i.e. reversed).
However, I don't know where to start. Any help is appreciated.
You can write a simple recursive predicate with a base case pattern matching on a list consisting of two elements like so:
last_two_reversed([X,Y],[Y,X]).
Since this is probably homework, I think it's best if you try to write the recursive clause yourself.
Simply use the built-in predicate reverse/2:
last_two_reversed([A,B|T],[Y,X]) :-
reverse([A,B|T],[Y,X|_]).
This will fail for lists with strictly less than two elements. A sensible thing to do would be to make it succeed using those two additional rules:
last_two_reversed([],[]).
last_two_reversed([H],[H]).
First of all, the predicate should fail or succeed with empty list or list with only one element??
In mathematical logic the predicate should return true with empty list and one-element list, because there are no last and second to-last elements to reverse.
So if you want to succeed with empty or one element list you should first start with :
lastTwoReversed([],[]).
lastTwoReversed([X],[X]).
(else don't write the above rules).
Next as base you should write:
lastTwoReversed([X,Y],[Y,X]).
and finally for list of length 3 or greater:
lastTwoReversed([X,Y,Z|T],[X|T1]):-lastTwoReversed([Y,Z|T],T1).
Keep in mind that we write [X,Y,Z|T] to specify for list with 3 or more elements so that doesn't match the previous rules.

Correctly building up a list in Prolog

I'm attempting to write a predicate to remove elements from an ordered list in Prolog. This is part of a homework assignment and I'm very confused about how Prolog's semantics work in general.
When I try the following function with the goal rdup([1,2], L). I get false. I've traced the goal and it looks like I'm not supposed to be building up the result list the way I am building it up with recursive calls to rdup. I'm not certain how I should be building up the result list. Here's the function:
rdup([],M).
rdup([X],[X]).
rdup([H1,H2|T], M) :- H1 \= H2, rdup(T, [M,H1,H2]).
rdup([H1,_|T], M) :- rdup(T, [M,H1]).
Can anyone tell me where my reasoning is wrong or how one is supposed to build up a list recursively in Prolog?
Firstly, it's not functions.
It's predicates, they only say, what's true, what's not, and under what conditions.
Here are your program:
rdup([],[]).
rdup([X],[X]).
rdup([H,H|T], M) :- rdup([H|T], M).
rdup([H1,H2|T], [H1|M]) :- H1 \= H2, rdup([H2|T], M).
Now a little bit of explanation.
Firstly, what says "rdup(X,Y)"? It's not saying "Take ordered list in X, and put list X without duplicates in Y", but it says "This fact will be true, if Y is an list X without duplicates, in assumption that X is an ordered list". Notice, that we don't talk about "returning values" or sth like that.
First line says, that the empty list is a list without duplicates of empty list. Quite obvious, right?
Next line is basically the same, but with one element.
Third line says, that if we have list, which consists from two same elements H and tail T, then the ordered list of this list ([H,H|T]) is the same, as it would be with only one element H. That's why we have "M" in both predicates unmodified.
I hope you will analyse the last predicate on your own, Prolog is not as hard as it looks to be. Good luck!

Override a list element?

Is there a way to add an element to a list, without caring if it already exists and the list still not containing duplicates?
If you are looking for efficiency there are better data structures than lists, ready to use in SWI-Prolog. In particular, your use case fits on add_nb_set(+Key, !Set, ?New).
Argument New controls the behaviour WRT duplicates: you should use true for don't care.
Argument Set must be 'allocated' with empty_nb_set(Set)
edit: I apologize, argument Set must be a free var for don't care behaviour, like for instance add_nb_set(Key, Set, _), as evident for the documentation page....
You could first check whether the item is contained in the list and add it only if it's not.
For example:
add_no_duplicates(List, Item, NList):-
member(Item, List) -> NList=List ; NList=[Item|List].
Test:
?- add_no_duplicates([a,b,c],d,L).
L = [d, a, b, c].
?- add_no_duplicates([a,b,c,d],d,L).
L = [a, b, c, d].
Note that add_no_duplicates/3 as written needs the input list (List) to be instantiated.
Basically you want to implement a set; check the list manual page for set operations.
It appears that there is no add but there is union/3 so you can add elements by intersecting with a set of the new element (intersection([NewEl], OldSet, NewSet). Note that you dont have to convert a list to a set (a list is a set as long as it doesnt have duplicates; list_to_set/2 just eliminates them).
Now if you have a list with duplicates and you want sometimes to add an element the way you stated you will have to implement something yourself.