Prolog compare list structure - list

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(...).

Related

Prolog: Matching elements in a specific between two lists

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.

Return a list from items matching in two lists Prolog

I am very new to Prolog and I have this :
compare_list(Hours1, Hours2, Matching)
I want to return the matching hours between lists Hours1 and Hours2 into the list Matching
I can get the matches but not construct the list of matches.
Hours1 may be like: [1,2,3],
Hours2 may be like: [2,3],
So from this:
Matching Hours should be: [2,3]
Help would be appreciated.
I have implemented what Vennik has suggested and it is very near to what I want.
Results From Hours1 : [2,3,5], Hours2 : [2,5]
Give the following:
Matching = [2, 5] ;
Matching = [2] ;
Matching = [5] ;
Matching = []
Is it possible to only have the first set without producing the other three results?
You might want to consider the related question intersection and union of 2 lists.
In particular, my logically pure answer to above question might be of good use to you, as it offers multiple advantages over the code posted by #vennik above:
The predicates behave the way relations should. "Multi-directional."
They are monotone and remain sound upon arbitrary generalization / specialization.
They aim at completeness of the relation of interest, creating choice points when required.
They are efficient, avoiding the creation of useless choice points.
Try this:
compare_list([], _, []).
compare_list([Hour | Hours1], Hours2, [Hour | Matching]) :-
member(Hour, Hours2),
compare_list(Hours1, Hours2, Matching).
compare_list([_ | Hours1], Hours2, Matching) :-
compare_list(Hours1, Hours2, Matching).
Calling compare_list([1,2,3], [1,2], X), !. will result in X = [1,2].
I know that is not pure... (or not montone, if you like) ... but, if you aren't a purist, SWI-Prolog give you the predicate
intersection/3
that you can use in this way
intersection(Hours1, Hours2, Matching).

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].

List search in Prolog

I'm trying to make a search in a list in Prolog, but I would like to start right at a certain position. Will I have to do that boring recursive search?
Thank you.
you can Skip N positions using N free_vars, and append/3 to get rid of list' header:
?- [user].
|: find(Needle, Haystack, Skip) :-
|: length(Header, Skip),
|: append(Header, Actual, Haystack),
|: memberchk(Needle, Actual).
|: % user://1 compiled 0,04 sec, 2 clauses
true.
?- find(3-X,[1-alfa,2-beta,3-gamma,4-delta,1-abba,2-bridge],1).
X = gamma.
?- find(3-X,[1-alfa,2-beta,3-gamma,4-delta,1-abba,2-bridge],3).
false.
?- find(3-X,[1-alfa,2-beta,3-gamma,4-delta,1-abba,2-bridge],2).
X = gamma.
If there are many search to to, the list build length(Header, Skip) it's better done once...