Prolog: Matching elements in a specific between two lists - list

I already have this code to match if any numbers in a list match any numbers in the other list. However, I need to modify my recursion so it returns false if the same number is found in the same position on both lists.
For example:
[5,3,4,6,2] is compatible with [3,1,2,2,7]; and [1,3,4,9,2] is incompatible with [4,5,2,9,8] because both lists have a 9 in the 4th position.
Here is the code I have so far:
common_elements([], L) :-
fail.
common_elements([H|T], L) :-
memberchk(H, L), !.
common_elements([H|T], L) :-
common_elements(T, L).
SWI-Prolog example inputs and outputs for current code:
?- common_elements([1,2,3,4],[6,7,8,9]).
false.
?- common_elements([1,2,3,6],[6,7,8,9]).
true.

As with (nearly) all predicates with list processing, you can split your predicate in two types of clauses:
base clauses: in many cases these lists deal with empty lists. Here this is for instance:
compatible([],_).
compatible(_,[]).
inductive clauses: in this case both lists are not empty. In that case you have to compare the heads of the two lists. If not equal, you make a recursive call:
compatible([HA|TA],[HB|TB]) :-
HA \= HB,
compatible(TA,TB).
in the recursive call, you simply use the tails of both lists.
Now merging both clauses:
compatible([],_).
compatible(_,[]).
compatible([HA|TA],[HB|TB]) :-
HA \= HB,
compatible(TA,TB).
You can make it a bit more effecient (and atomic by using a cut):
compatible([],_) :-
!.
compatible(_,[]).
compatible([HA|TA],[HB|TB]) :-
HA \= HB,
compatible(TA,TB).
Demo (swipl):
?- compatible([5,3,4,6,2],[3,1,2,2,7]).
true.
?- compatible([1,3,4,9,2],[4,5,2,9,8]).
false.

Related

Prolog predicate to find intersection of 2 lists outputting list of G followed by numbers [G2252,G2255]

correct_letters([],L2,[]).
correct_letters([L1|T1],L2,[L3|T3]) :-
list_helper1(L1,L2),
correct_letters(T1,L2,T3).
correct_letters([L1|T1],L2,CL) :-
\+ list_helper1(L1,L2),
correct_letters(T1,L2,CL).
list_helper1(F,[F|_]).
list_helper1(F,[_|T]) :-
list_helper1(F,T).
This code to return the intersection of two lists returns G followed by numbers. For example when inputs [1,2,3,4] and [1,3] are given output is CL = [_G2252, _G2255] . how do I make the output normal letters and numbers
_G2252 and similar are unbound variables, they are variables you have named without giving Prolog any reason to bind them to a value.
how do I make the output normal letters and numbers
L3 should be L1 in this line:
correct_letters([L1|T1],L2,[L3|T3]) :-
at the moment it says "if L1 is found in L2, put an unknown variable in the third list". It should say "if L1 is found in L2, put L1 in the third list".

Prolog compare list structure

I am trying to compare two lists in prolog like
?- compare([[X],Y,Z],[[A],B,C]).
true.
?- compare([X,Y,Z],[[A],B,C]).
false.
I can't use the ==/2 operator because the logical variables aren't the same,
so this would evaluate false for both cases.
And I obviously cant use =/2 because this would evaluate true in both cases.
Any suggestions on how to solve this problem?
An alternative solution to check if two terms have the same structure is to use the ISO Prolog standard predicate subsumes_term/2. The following predicate, variant/2 is true when a term is a variant of another term:
variant(Term1, Term2) :-
\+ \+ subsumes_term(Term1, Term2),
\+ \+ subsumes_term(Term2, Term1).
The use of double negation avoids instantiating any variables in the arguments. Using e.g. GNU Prolog for testing:
| ?- variant([[X],Y,Z],[[A],B,C]).
(1 ms) yes
| ?- variant([X,Y,Z],[[A],B,C]).
no
If you're just trying to match up list structures, perhaps something like this:
compare([H1|T1], [H2|T2]) :-
is_list(H1),
is_list(H2),
compare(H1, H2),
compare(T1, T2).
compare([H1|T1], [H2|T2]) :-
\+ is_list(H1),
\+ is_list(H2),
compare(T1, T2).
compare([], []).
| ?- compare([[X],Y,Z],[[A],B,C]).
true ? ;
no
| ?- compare([X,Y,Z],[[A],B,C]).
no
The nice thing about this solution is that it will work whether the variables are instantiated or not.
| ?- compare([[a],b,c], [[d],X,f]).
true ? ;
no
| ?-
this works for the the presented test case
elemcmp(A,B) :- var(A), var(B), ! ; A =#= B.
?- maplist(elemcmp,[[X],Y,Z],[[A],B,C]).
true.
?- maplist(elemcmp,[X,Y,Z],[[A],B,C]).
false.
You can use (SWI Prolog, at least), =#=/2. A test like Term1 =#= Term2 is true
if Term1 is a variant of (or structurally equivalent to) Term2.
Testing for a variant is weaker than equivalence (==/2),
but stronger than unification (=/2).
Two terms A and B are variants iff [sic] there exists a renaming of the variables
in A that makes A equivalent (==) to B and vice versa.
Basically, foo(A,B) and foo(B,C) and foo(C,D) are are variants of each other, but not of foo(A,A).
Tests against anonymous variable behaves as you might expect: foo(_,_) is a variant of foo(A,B), but not of foo(A,A) (since all usages of the anonymous variable are distinct).
Here is another solution:
compare(A, B) :- (var(A); var(B)), !, var(A), var(B).
compare([], []).
compare([H1|T1], [H2|T2]) :- compare(H1, H2), compare(T1, T2).
To also accept atoms, use \+ is_list(...) or equivalent instead of var(...).

Exactly one pair in Prolog list

Does anybody know how I can go about determining/ensuring that there is exactly one duplicate element in a prolog list?
I am studying for a test.
Sort the list using sort/2. It removes duplicates, so if the sorted list is exactly one shorter, you had exactly one pair.
one_duplicate(L) :-
sort(L, Sorted),
length(L, Len),
length(Sorted, SortedLen),
Len =:= SortedLen + 1.
Finding the duplicate pair is another question altogether.
one_duplicate(L) :-
sort(L, Sorted),
length(L, Len),
length(Sorted, SortedLen),
Len =:= SortedLen + 1.

How to check if each element in Prolog list is greater than 0?

I have the following Prolog predicate prototype: solution(+InputVector), where InputVector is a list of values of an unknown length. If all the values in the list are greater than 0, I print out a message. How do I do this?
if you are interested to extend your learning to 'higher order' predicates, consider maplist/2:
3 ?- maplist(<(0), [1,2,3]).
true.
4 ?- maplist(<(0),[0,1,2,3]).
false.
Try checking if the list is [] or [X|Xs], and act accordingly.
You were nearly there. Consider the following (updated thanks to #aBathologist):
1| solution([X]) :-
2| X > 0,
3| !,
4| write_ln('Success!').
5| solution([X|Y]) :-
6| X > 0,
7| solution(Y).
Let's consider how this actually works, line-by-line:
Defines the first clause of the predicate solution/1 which takes a single-element list containing X as an argument.
Tests if item X is a number which is > 0. If not, the predicate will terminate here and fail. Otherwise, Prolog will continue to the next line.
While not strictly necessary, the cut (!) here removes the choice-point Prolog would have generated at (1) given that the second clause on line (6) could also have been executed with the input [X], as this is equivalent to [X|Y] where Y = []. Therefore, this is a so-called 'grue'-cut for efficiency only.
As the list [X] contains elements all greater than zero, the predicate prints a message and suceeds.
Defines the second clause of the predicate solution/1 which takes a list containing one or more items, where X is the head of the list, and Y is the tail (remainder) of the list (which is itself a list which could be empty: []).
Same as line (2).
Proceed to recursively test the remainder of the list, Y.
Note that the above definition assumes that solution/1 must only succeed on non-empty lists of numbers greater than zero. If you wish to permit this predicate to succeed on empty lists, the implementation can be made even simpler:
solution([]) :-
write_ln('Success!').
solution([X|Y]) :-
X > 0,
solution(Y).
In this version, either one of the two clauses of solution/1 is executed based on the argument: the first handles empty lists, and the second handles non-empty lists of numbers which are greater than zero. A cut (!) is not necessary here as the predicate arguments are non-unifiable ([] \= [X|Y]) and Prolog will not generate choice-points for any invocation of the first clause.
I hope this has been helpful, and has made some of the semantics of Prolog syntax clearer for you.
I'd write the predicate like this:
all_greater_than_zero([]).
all_greater_than_zero([H|T]) :-
H > 0,
all_greater_than_zero(T).
I assumed that an empty list was acceptable. If not, you can remove the first clause.

Prolog, working with multiple predicates

I've just starting working with Prolog and I don't understand how to work with multiple predicates.
For example I have to solve the following problem: Substitute in a list a value with all the elements of another list.
This is the code that I managed to write so far:
domains
elem=integer
list=elem*
predicates
%append to a list already created another list. There are 3 list parameters
%because I don't know other method
append (list,list,list)
%This will search the list (the first one) for the searched element and
%it is it will replace it with the list(the second one). The result will be
%kept in the third list.
add(list,list,elem,list)
goal
add([1,2,3,2,1],[4,5],2,L),
write (L).
clauses
add ([],[_],_,[]).
add ([A|L],L1,E,[A|L2]):-
add(L,L1,E,L2).
add ([E|L],L1,E,L2):-
add(L,L1,E,L2).
append([],[],L2).
append([],[X|L1],[X|L2]):-
append([],L1,L2).
Does your append definition is working? I think should be
append([], L, L).
append([X|Xs], Ys, [X|Zs]):-
append(Xs, Ys, Zs).
The append predicate it's one of the most basic tools in Prolog programming, better to keep the usual behaviour, or to change name...
Instead of add, a better name could be replace_elem_with_list.
To implement it you should iterate, inspecting each element, and when you find a match to what's required to replace append the list instead of copying the element.
Something like
% replace_elem_with_list(ToSearch, Replacement, SoughtElem, Result)
replace_elem_with_list([E|Es], Replacement, E, Result) :-
!, replace_elem_with_list(Es, Replacement, E, Rs),
append(Replacement, Rs, Result).
I'll leave you the other 2 cases that you will need to cover (when element doesn't match and recursion base, which are similar to append)
the result:
?- replace_elem_with_list([1,2,3,4,2,3,4],[a,b],2,L).
L = [1, a, b, 3, 4, a, b, 3, 4].