Prolog, working with multiple predicates - list

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

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

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 problem help

I am trying to learn prolog and have come across a problem that I can't figure out. The problem is to write a prolog predicate that takes all the numbers of a list that are less than a given number and put them into a list that will be returned. For example:
input:
findNum(5, [5, 3, 5, 6, 4, 5], Y)
output:
Y = [3, 4]
Everything I've tried seems to fail. So any help would be much appreciated. Thanks.
To solve this, you will use a typical Prolog pattern of examining the elements from your input list one-at-a-time. Prolog includes a syntax for selecting the head element from a list, by unifying the list with [A | B] , the first element of the list is unified with A and the remainder of the list (or emptiness if no elements remain) is unified with B.
You should consider first how many clauses you will need. You will need one clause to handle the case of an empty list, which is also the termination condition for your recursion. Each time you examine one item of the list, you recursively examine the remainder of the list. On the final examination, the 'remainder of the list' is empty.
For the clauses which examine the head element of the list, you have two possible conditions: the element satisfies your search criterion (less than 'num'), or it does not. To represent this, implement two clauses, both of which iterate over the list, but only the first of which matches your search criteria. The clause which detects "matching" elements must be written first in your Prolog file so that it will be considered first.
% This clause is for the "empty input" case which is also used to
% discontinue the recursion when finished.
findNum( _, [], []).
% This clause completes only if the first input element is less than
% 'M'. If the clause completes, the first input element ('X') is unified
% with the output list ([X | Y]).
findNum( M, [X | L], [X | Y]) :-
X < M,
findNum(M, L, Y).
% This clause completes if the above clauses do not. Much like an "else"
% case, this clause simply removes the first input element from the list,
% the element must not match in the search clause above and so we will now
% discard it. Thus, it is unified with the "throw away" variable named "_"
findNum( M, [_ | L], Y) :-
findNum(M, L, Y).