Append to list if specific word is entered - prolog

I am trying to append a list and a word together, and if the user types a specific word I want to add a certain letter to the list.
For example, I want to make the words entered in a list change based on the pronoun.
?- append([t,a,l,k], she, X).
X = [t, a, l, k, s].
so if the user enters [t, a, l, k] and she, Prolog will add 's' to the end of the list.
The code I have so far is only able to append the two entered values and not based on if the user enters a certain word.
append( [], X, X).
append( [A | B], C, [A | D]) :- append( B, C, D).
result:
?- append([t,a,l,k], she, X).
X = [t, a, l, k|she].
How can I make it so if they type she prolog adds 's' to the list instead of 'she'?
Thank you.

You have to decompose the atom she into individual characters first.
It is also best to use my_append/3 because append/3 already exists.
my_append( [], W, [F]) :- atom_chars(W,[F|_]).
my_append( [A | B], W, [A | D]) :- my_append(B, W, D).
:- begin_tests(shemanator).
test("append 'she'", true(X == [t, a, l, k, s])) :-
my_append([t,a,l,k], she, X).
test("append 'she' to an empty list", true(X == [s])) :-
my_append([], she, X).
test("append 's'", true(X == [t, a, l, k, s])) :-
my_append([t,a,l,k], s, X).
:- end_tests(shemanator).
And so
?- run_tests.
% PL-Unit: shemanator ... done
% All 3 tests passed
true.

Related

Get set of elements from list (Prolog)

I am trying to get a set of elements from a list in prolog, such that a query:
get_elems([1, 2, 4, 10], [a, b, c, d, e], X).
yields:
X = [a, b, d]
I would like to implement it without using the built in predicate nth.
I have tried using the following, but it does not work:
minus_one([], []).
minus_one([X|Xs], [Y|Ys]) :- minus_one(Xs, Ys), Y is X-1.
get_elems([], _, []).
get_elems(_, [], []).
get_elems([1|Ns], [A|As], Z) :- get_elems(Ns, As, B), [A|B] = Z.
get_elems(Ns, [_|As], Z) :- minus_one(Ns, Bs), get_elems(Bs, As, Z).
Edit: The list of indices is guaranteed to be ascending, also I want to avoid implementing my own version of nth.
Give this a go:
get_elems(Xs,Ys,Zs) :- get_elems(Xs,1,Ys,Zs).
get_elems(Xs,_,Ys,[]) :- Xs = []; Ys = [].
get_elems([N|Xs],N,[H|Ys],[H|Zs]) :- !, N1 is N + 1, get_elems(Xs,N1,Ys,Zs).
get_elems(Xs,N,[_|Ys],Zs) :- N1 is N + 1, get_elems(Xs,N1,Ys,Zs).
This just keeps counting up and when the head of the second term is equal to the current index it peels off the head and makes it the head of the current output term. If it doesn't match it just discards the head and keeps going.

Generate all words of length N and form a list with them in Prolog

Given the letters [a, b, c] generate the list containing all the words of length N, formed out of this letters.
For example:
?- generate(2, L).
should output:
L = [aa, ab, ac, ba, bb, bc, ca, cb, cc].
At first, this seemed like a pretty simple problem, but I've discovered that none of my implementations work.
This is the second implementation, the one that kind of works.
letter(X) :- member(X, [a, b, c]).
generateWord(0, []) :- !.
generateWord(N, [H|T]) :-
letter(H),
NextN is N - 1,
generateWord(NextN, T).
generateAtomicWord(N, Word) :-
generateWord(N, WList),
atomic_list_concat(WList, Word).
maxSolutions(N, R) :- R is N ** 3.
generate(N, CurrentList, ResultList) :-
maxSolutions(N, R),
length(CurrentList, L),
L =:= R,
append(CurrentList, [], ResultList), !.
generate(N, CurrentList, ResultList) :-
generateAtomicWord(N, NewWord),
\+ member(NewWord, CurrentList),
append(CurrentList, [NewWord], NewList),
generate(N, NewList, ResultList).
generate(N, ResultList) :-
generate(N, [], ResultList).
It kind of works because when given N = 3 the program outputs:
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...]
My first implementation is different, but I can't make it work on any case.
letter(X) :- member(X, [a, b, c]).
generateWord(0, []) :- !.
generateWord(N, [H|T]) :-
letter(H),
NextN is N - 1,
generateWord(NextN, T), !.
generateAtomicWord(N, Word) :-
generateWord(N, WList),
atomic_list_concat(WList, Word).
maxSolutions(N, R) :- R is N ** 3.
generate(N, [H]) :- generateAtomicWord(N, H).
generate(N, [H|T]) :-
generate(N, T),
length(T, TailLen),
maxSolutions(N, M),
(TailLen =:= M -> !;
generateAtomicWord(N, H),
\+ member(H, T)).
This one just outputs:
L = [aa]
and when requested for the rest of the solutions it cycles.
The problem must be solved without using predicates such as:
findall, findnsol, bagof, setof, etc...
that find all the solutions.
I've added the tag backtracking because it does resemble a backtracking problem, but I've no idea what a standard implementation might look like in Prolog.
It kind of works because when given N = 3 the program outputs:
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...]
That is not an error, that is the Prolog interpreter that displays the list in a shorter way. If you hit w when it shows the output, it will show the full list. For more information see this answer.
That being said, you make it too hard. You can first make a predicate that will unify a variable with all possible atoms:
letter(X) :- member(X, [a, b, c]).
word(0, []).
word(N, [C|W]) :-
N > 0,
N1 is N-1,
letter(C),
word(N1, W).
Now we can generate all possibilities with findall/3 [swi-doc], and use for example maplist/3 [swi-doc] with atomic_list_concat/2 to convert the list to a single atom:
words(N, L) :-
findall(W, word(N, W), Ws),
maplist(atomic_list_concat, Ws, L).
For example:
?- words(0, L).
L = [''].
?- words(1, L).
L = [a, b, c].
?- words(2, L).
L = [aa, ab, ac, ba, bb, bc, ca, cb, cc].
?- words(3, L).
L = [aaa, aab, aac, aba, abb, abc, aca, acb, acc|...].
We can generate a list of lists ourselves by updating a "difference" list until all possible words are generated:
wordlist(N, L) :-
wordlist(N, [], L, []).
wordlist(0, R, [W|T], T) :-
reverse(R, W),
!.
wordlist(N, C, L, T) :-
N > 0,
N1 is N-1,
wordfold([a,b,c], N1, C, L, T).
wordfold([], _, _, L, L).
wordfold([C|CS], N1, CT, L, T) :-
wordlist(N1, [C|CT], L, L2),
wordfold(CS, N1, CT, L2, T).
For example:
?- wordlist(0, L).
L = [[]].
?- wordlist(1, L).
L = [[a], [b], [c]].
?- wordlist(2, L).
L = [[a, a], [a, b], [a, c], [b, a], [b, b], [b, c], [c, a], [c|...], [...|...]].
You then still need to perform atomic_list_concat on it. I leave that as an exercise.

Prolog: how can I change the output of combinations(N, [H|T], P) to return a list of pairs, rather than just the first one before ;?

Prolog: How can I change the output of combinations(N, [H|T], P) to return a list of pairs, rather than just the first one before ; ? The program works well as long as I press ; in the command line, but I want to return directly a list of pairs.
comb(1, [H|_], [H]).
comb(N, [H|T], [H|C]) :- N1 is N - 1, N1 > 0, comb(N1, T, C).
comb(N, [_|T], C):- comb(N, T, C).
This is my program. Thank you very much!
You are looking for findall/3.
findall(+Template, :Goal, -Bag)
Create a list of the instantiations Template gets successively on backtracking over Goal and unify the result with Bag. Succeeds with an empty list if Goal has no solutions. findall/3 is equivalent to bagof/3 with all free variables bound with the existential operator (^), except that bagof/3 fails when Goal has no solutions.
Example:
?- findall(X, comb(2, [a,b,c,d], X), Xs).
Xs = [[a, b], [a, c], [a, d], [b, c], [b, d], [c, d]].

Prolog Zip Function

Im in rew to Prolog. I'm trying to write a zip function. The question goes like this.
zip(L1, L2, X): The list X is formed by “zipping” the first 2 arguments.
the result should be like this:
?- zip([a, b, c], [x, y, z], X).
L = [a, x, b, y, c, z]
?- zip([a, b], [x, y, z], X).
false
?- zip([a, b, c, d], X, [a, p, b, q, c, r, d, s]).
X = [p, q, r, s]
I have done this so far.
I can get the result for 1st 3rd but not the 2nd one. Can anybody can help me solving it for the 2nd one? thank you
zip([X],[Y],[X,Y]).
zip([], [], []).
zip([X|Xs], [Y|Ys], [X,Y|Zs]) :-
zip(Xs,Ys,Zs).
zip([X|Xs],[],[X|Xs]).
zip([Y|Ys],[],[Y|Ys]).
zip(Xs, [], Xs).
zip([], Ys, Ys).
How do I define this function where:
allsame(L): The list L contains identical elements.
I should get this.
?- allsame([b, b, b]).
true
?- allsame([c, c, c, Y, c, c, X, c]).
X = c, Y = c
You had it:
zip([], [], []).
zip([X|Xs], [Y|Ys], [X,Y|Zs]) :- zip(Xs,Ys,Zs).
This alone is enough to define the relation you're seeking. The extra clauses don't help.
Test:
?- zip([a, b, c], [x, y, z], X).
X = [a, x, b, y, c, z].
?- zip([a, b], [x, y, z], X).
false.
?- zip([a, b, c, d], X, [a, p, b, q, c, r, d, s]).
X = [p, q, r, s].
#m09 gave the correct answer. But I'd like to explain why what you have isn't correct:
(1) zip([X],[Y],[X,Y]).
This rule says that [X,Y] is what you get when you zip [X] with [Y]. That is correct, and will not lead to a problem. The rule is simply redundant with the rules below (which I'll explain...).
(2) zip([], [], []).
This rule says [] is what you get when you zip [] with [] which is correct and as simple a rule as you can have for zip.
(3) zip([X|Xs], [Y|Ys], [X,Y|Zs]) :-
zip(Xs,Ys,Zs).
This rule says that [X,Y|Zs] is what you get when you zip [X|Xs] with [Y|Ys] if Zs is what you get when you zip Xs with Ys. That is also logical and correct. Notice that zip([X], [Y], [X,Y]) is zip([X|[]], [Y|[]], [X,Y|[]]). so it can be derived from rules (2) and (3). It would match rule (3) first, zip([X|[]], [Y|[]], [X,Y|Zs]) :- zip([], [], Zs)., then Zs would become [] by rule (2)`.
(4) zip([X|Xs],[],[X|Xs]).
(5) zip([Y|Ys],[],[Y|Ys]).
Rule (4) says [X|Xs] is what you get when you zip [X|Xs] with []. Rule (5) says exactly the same thing, logically, only with a different variable name. These are incorrect, since that would mean, for example, zip([a,b,c], [], Z) would be true if Z = [a,b,c].
(6) zip(Xs, [], Xs).
This rule says that Xs is what you get when you zip Xs with []. Or stated another way, any input, zipped with [], would be that input value again. It wouldn't even have to be a list! This is clearly incorrect. Queries like zip(x, [], Z) would succeed with Z = x, and zip(friend(bill,mary), [], Z) would succeed with Z = friend(bill,mary).
(7) zip([], Ys, Ys).
This rule says that Ys is what you get when you zip [] with Ys. It is incorrect for the same reason (6) is incorrect. In fact, this rule, combined with (2) and (3) are why the query zip([a, b], [x, y, z], X). will yield a result rather than fail. Rules (2) and (3) will recurse to zip([b], [y,z], [b,y|T]) :- zip([], [z], T). and then zip([], [z], T) will finally succeed on rule (7) with T = [z], and ultimately yielding a final result to zip([a, b], [x, y, z], X) of X = [a, x, b, y, z].

Prolog , Append with no repititions

Hey I'm trying to append two list with no "double" members
for example
A = [a, b, c]
B = [x, c, q]
then ->
append2(A,B,P)
P= [a,b,c,x,q]
I write this code, but it doesn't work...
not_member(_, []).
not_member(X, [Y|Ys]) :- X \= Y, not_member(X, Ys).
append2(A, [], A).
append2([], A, A).
append2([h1|ls], B, [h1|P]) :- not_member(h1, B), !, append2(ls, B, P).
append2([h1|ls], B, P) :- member(h1, P), append2(ls, B, P).
Thanks for helping :)
Assuming there are no variables in your input lists, but allowing duplicates in each list you may write:
append2(A,B,C):-
findall(Item, append2_item(A,B,Item), C).
append2_item(A,_,ItemA):-
append(HeadA, [ItemA|_], A),
\+ member(ItemA, HeadA).
append2_item(A,B,ItemB):-
append(HeadB, [ItemB|_], B),
\+ member(ItemB, HeadB),
\+ member(ItemB, A).
First clause of append2_item/3 selects (ordered) distinct items from the first list. Second clause of append2_item/3 selects (ordered) distinct items from the second list which are not present in the first list.
append2/3 just collects those elements.
Test case:
?- append2([a,b,c,a],[x,c,q,x],C).
C = [a, b, c, x, q].
Check out the pure code in my answer
to the related question "intersection and union of 2 lists"!
Telling from your requirements, predicate list_list_union/3 is just what you are looking for:
?- list_list_union([a,b,c],[x,c,q],Ls).
Ls = [a,b,c,x,q]. % succeeds deterministically
list_list_union/3 is monotone, so we get sound answers
even when using non-ground terms:
?- As = [_,_,_], Bs = [_,_,_], list_list_union(As,Bs,Ls), As = [a,b,c], Bs = [x,c,q].
As = [a,b,c], Bs = [x,c,q], Ls = [a,b,c,x,q] ; % logically sound result
false.

Resources