Prolog - How to replace certain words in a list - prolog

I have the following prolog predicate:
processWords([hello, my, name, is, Simon], Result).
I need to know how I could get:
Result = [bye, my, name, is, Ben]
How would I recurse through the list and return a string that replaces "hello" with "bye" and "Simon" with "Ben", using Prolog?
Any help is greatly appreciated, thanks!

First of all, this cannot be done, since "Simon" and "Ben" in your example are variables. But supposing you are fine with "simon" and "ben", here is an answer:
processWords([], []).
processWords([H|T], [H2|T2]) :-
translate(H, H2),
processWords(T, T2).
translate(hello, bye):-!.
translate(simon, ben):-!.
translate(X, X). % catch-all clause for all words not to be translated
Alternatively, you could use maplist/3:
processWords(L,L2):-maplist(translate, L, L2).
translate(hello, bye):- !.
translate(simon, ben):- !.
translate(X, X). % catch-all clause for all words not to be translated

Related

Remove Item from List of Lists

I have a list containing records which have an Id, name and gender. I am trying to have a user pass in either the Id (an integer) or name (a string), and removing that record, but it's not doing anything.
This is what i have:
choice(5,X) :-
write('\tEnter an ID or Name:'),
read(Item),
rem(Item, X, X2),
menu(X2).
and my remove is:
rem(Item, [], []) :- write("List is empty.").
rem(Item, [[Item,RT]|L],L).
rem(Item, [[ID, Item|PT]|L]|L).
rem(Item, [X|XT],[X|YT]) :- rem(Item, XT, YT).
But when i run it, it just gives me a list of integers.
Any help would be much appreciated!
May be I misunderstood the problem, but I think that
rem(Item, L_In, L_Out) :-
select([Item|_], L_In,L_Out);select([_,Item|_], L_In, L_Out).
does the job.
Your code is (almost) working fine.
You just had a typo in the third rule: you wrote a | instead of a ,, and the prolog interpreter lets you know with:
Warning: user://1:18:
Clauses of (rem)/3 are not together in the source-file
You may want to remove the write("List is empty."), because that rule non only matches when you try to remove an item from an empty list, but it is also the base case of the recursive delete. It prints numbers because you supply it with a string ("abc") instead of an atom ('abc'). Compare the result of write('abc') with the result of write("abc").
Also, you need a cut in the removing rule, because if the item exists in the list, you want to remove it, and you are not interested in other solution arising from backtracking from that choice point.
rem(Item, [], []).
rem(Item, [[Item,RT]|L],L) :- !.
rem(Item, [[ID, Item|PT]|L],L) :- !.
rem(Item, [X|XT],[X|YT]) :- rem(Item, XT, YT).
Test:
?- rem(x, [[a,3],[b,4],[remove_me,x,6],[d,8]], X).
X = [[a, 3], [b, 4], [d, 8]].

Match database items exactly once in Prolog?

Let's say there is a simple database of people in Prolog
person(john).
person(mary).
person(john).
person(susan).
I need to match the entires exactly once:
john-mary, john-john, john-susan, mary-john, mary-susan, john-susan
I tried coming up with something like this:
match:- person(X),!,person(Y), write(X),write(-), write(Y),nl.
run:- person(X), match(X), fail.
But it's matching many times, and matches a person to him/herself, which shouldn't be.
Basically, what I need is to iterate over all Xs and make Prolog to look strictly "below" for Ys.
A quick solution would be to number your people:
person(1, john).
person(2, mary).
person(3, john).
person(4, susan).
Then you could match people like this:
match(X-Y) :-
person(I, X), person(J, Y), I < J.
Since you have two john entries, I'm not sure any other solution is going to work. Normally you could fake an ordering using #>/2 but that would require your atoms to be unique, and since they aren't, it would prevent the john-john solution.
Edit: Since we're willing to use findall/3 to materialize the database of people, we can treat this as a list problem and find a functional solution. Let's get all the combinations in a list:
combinations([X|Rest], X, Y) :- member(Y, Rest).
combinations([_|Rest], X, Y) :- combinations(Rest, X, Y).
With this predicate in hand, we can find the solution:
combined_folks(People) :-
findall(P, person(P), Persons),
findall(X-Y, combinations(Persons, X, Y), People).
?- combined_folks(X).
X = [john-mary, john-john, john-susan, mary-john, mary-susan, john-susan].
That actually turned out to be pretty clean!
person(john).
person(mary).
person(john).
person(susan).
match :- findall(P,person(P),People), match_all(People).
match_all([_]) :- !.
match_all([P|People]) :- match_2(P,People), match_all(People).
match_2(_,[]) :- !.
match_2(P1,[P2|People]) :- format('~a-~a~n',[P1,P2]), match_2(P1,People).
?- match.

Replace atom with variable

I have a term which may or may not contain the atom 'this'. The term may also contain variables.
I need to replace 'this' with a variable I. How can I do this?
I tried to do something like this:
term_to_atom((f(a), g(this, b), ...), A),
tokenize_atom(A, L),
replace(this, I, L, L2)
It seemed to work. The problem is, I need to go back to the original term and I can't do it...
SWI-Prolog has atomic_list_concat/2 and atom_to_term/2 which should help you go back to the original term.
main :-
term_to_atom((f(a), g(this, b)), A),
tokenize_atom(A, L),
replace(this, 'I', L, L2),
atomic_list_concat(L2, A2),
atom_to_term(A2, T, []),
writeln(T).
?- main.
f(a),g(_G69,b)
true .
Take a look at this predicate (replace/4):
replace(Term,Term,With,With) :-
!.
replace(Term,Find,Replacement,Result) :-
Term =.. [Functor|Args],
replace_args(Args,Find,Replacement,ReplacedArgs),
Result =.. [Functor|ReplacedArgs].
replace_args([],_,_,[]).
replace_args([Arg|Rest],Find,Replacement,[ReplacedArg|ReplacedRest]) :-
replace(Arg,Find,Replacement,ReplacedArg),
replace_args(Rest,Find,Replacement,ReplacedRest).
An example of what you need:
| ?- replace(f(1,23,h(5,this)),this,Var,Result).
Result = f(1,23,h(5,Var))
yes

Prolog unify Lists inside a list

I'm trying to define a relation over lists...
?- matrix_items([[a,b],[c,d],[e,f]],Rs).
Rs = [a,b,c,d,e,f]. % expected result
So far, I was able to do something like this; unfortunately it doesn't add up all the elements:
sift([],_).
sift([H|T],[H|Result]) :-
create(H,Result),
sift(H,Result).
create([],_).
create([H|T],[H|R]) :-
create(T,R).
Hope hear from you soon.
Try something like this. I've changed the name of the predicate to flatten_l as unify has other connotations in Prolog:
flatten_l([H|T], FL):-
flatten_l([H|T], [], FL).
flatten_l([], FL, FL):- !.
flatten_l([H|T], ML, FL):-
flatten_l(T, ML, NL),
!,
flatten_l(H, NL, FL).
flatten_l(X, FL, [X|FL]).
Note also that this predicate will give you a stack overflow error if the first argument is uninstantiated...
If you want to collapse all of the lists (even sub-lists), you can use flatten/2.
If you only want to collapse a single level, then the following should work:
unify([], []).
unify([X|Xs], Ret) :- unify(Xs, Rs), append(X, Rs, Ret).
If you use SWI-pl, you can call flatten/2 to flatten all levels of nesting or append/2 to flatten only one level.

Prolog difference routine

I need some help with a routine that I am trying to create. I need to make a routine that will look something like this:
difference([(a,b),(a,c),(b,c),(d,e)],[(a,_)],X).
X = [(b,c),(d,e)].
I really need help on this one..
I have written a method so far that can remove the first occurrence that it finds.. however I need it to remove all occurrences. Here is what I have so far...
memberOf(A, [A|_]).
memberOf(A, [_|B]) :-
memberOf(A, B).
mapdiff([], _, []) :- !.
mapdiff([A|C], B, D) :-
memberOf(A, B), !,
mapdiff(C, B, D).
mapdiff([A|B], C, [A|D]) :-
mapdiff(B, C, D).
I have taken this code from listing(subtract).
I don't fully understand what it does, however I know it's almost what I want. I didn't use subtract because my final code has to be compatible with WIN-Prolog... I am testing it on SWI Prolog.
Tricky one! humble coffee has the right idea. Here's a fancy solution using double negation:
difference([], _, []).
difference([E|Es], DL, Res) :-
\+ \+ member(E, DL), !,
difference(Es, DL, Res).
difference([E|Es], DL, [E|Res]) :-
difference(Es, DL, Res).
Works on SWI-PROLOG. Explanation:
Clause 1: Base case. Nothing to diff against!
Clause 2: If E is in the difference list DL, the member/2 subgoal evaluates to true, but we don't want to accept the bindings that member/2 makes between variables present in terms in either list, as we'd like, for example, the variable in the term (a,_) to be reusable across other terms, and not bound to the first solution. So, the 1st \+ removes the variable bindings created by a successful evaluation of member/2, and the second \+ reverses the evaluation state to true, as required. The cut occurs after the check, excluding the 3rd clause, and throwing away the unifiable element.
Clause 3: Keep any element not unifiable across both lists.
I am not sure, but something like this could work. You can use findall to find all elements which can't be unified with the pattern:
?- findall(X, (member(X, [(a,b),(b,c),(a,c)]), X \= (a,_)), Res).
gets the reply
Res = [ (b, c) ]
So
removeAll(Pattern, List, Result) :-
findall(ZZ109, (member(ZZ109, List), ZZ109 \= Pattern), Result).
should work, assuming ZZ109 isn't a variable in Pattern (I don't know a way to get a fresh variable for this, unfortunately. There may be a non-portable one in WIN-Prolog). And then difference can be defined recursively:
difference(List, [], List).
difference(List, [Pattern|Patterns], Result) :-
removeAll(Pattern, List, Result1),
difference(Result1, Patterns, Result).
Your code can be easily modified to work by making it so that the memberOF predicate just checks to see that there is an element in the list that can be unified without actually unifying it. In SWI Prolog this can be done this way:
memberOf(A, [B|_]) :- unifiable(A,B,_).
But I'm not familiar with WIN-PRolog so don't know whether it has a predicate or operator which only tests whether arguments can be unified.

Resources