List search in Prolog - list

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

Related

Prolog pattern match and return

New to Prolog here and already found it hard.
And here's the question: let's say I got a char list such as [h,e,l,l,o] and a unicode number 108(which is the letter "l").
I'm trying to write a function that take the elements of the list one by one and pattern match with the given unicode number. Once matched, return the reste of the list.
Worked on it for a whole day now , almost tried everything I can think of and I still didn't figure out how to make it work. Anyone got some ideas?
function([C|Chars], CutoffCode, Chars) :-
char_code(C, CutoffCode).
function([C|Chars], CutoffCode, Result) :-
not(char_code(C, CutoffCode)),
function(Chars, CutoffCode, Result).
e.g.
?- function([h,e,l,l,o], 108, Result).
Result = [l, o]
?- function([h,e,l,l,o], 109, Result).
false

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.

Prolog: Adding elements to list through read/1

I am trying to add user input to a list with add_read_list/2 until the word "end." is encountered. This is how the output is supposed to look:
add_read_list(Resultlist, [a,b]).
|: c.
|: d.
|: e.
|: f.
|: end.
Resultlist = [f,e,d,c,a,b].
This is how far I have come:
add_read_list(Resultlist,Entrylist) :-
read(end), append([], Entrylist, Resultlist).
add_read_list(Resultlist, Entrylist) :-
read(Input), append([Input], Entrylist, X),
add_read_list(Resultlist, X).
But this way every second input gets skipped:
add_read_list(Resultlist, [a,b]).
|: c.
|: d.
|: e.
|: f.
|: end.
Resultlist = [f,d,a,b].
What am I doing wrong?
drop the first clause, and use the simple 'if/then/else' construct:
add_read_list(Resultlist, Entrylist) :-
read(Input),
( Input = end
-> reverse(Resultlist, Entrylist)
; add_read_list(Resultlist, [Input|Entrylist])
).
instead of appending each item read, 'cons' it, and reverse the full list when done (well, simply unify to get the list in LIFO, as seems required. i.e. instead of reverse(Resultlist, Entrylist), Resultlist = Entrylist)
The actual error is read(end). This will only succeed, if the term read in is end. Otherwise it fails.
But I do have a lot of reservation to tell you this at all, since nobody every does this is Prolog. Here are the actual things that would need improvement.
The first, is to use end_of_file in place of end, because this is automatically generated at the end of a file, plus you can write it yourself, too.
The second is the way how to handle this. You need to read and then process the term read in:
... read(X), ( X == end_of_file -> ... ; /* whatever */ ... ), ....
However, please do reconsider your entire task. There is a single reason why this could be needed: When you are processing a Prolog file. But this you get cheaper and less error prone by saying [file].
So better design your data to fit into Prolog terms.

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

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