How to make my relation work - prolog

I have the following relation: index(X,N,List).
for example:
index(X,2,[a,b,c]).
X=b
index(b,N,[a,b,c]).
N=2
I don't know how to make my relation to work with the second example. It says that N is not defined well
Here is my code (it works well for the first example).
index(X,1,[X|_]).
index(X,N,[_|Tail]) :- N > 1, N1 is N - 1 , index(X,N1,Tail).

There is a SWI-Prolog built-in nth1/3 that does what you want:
?- nth1(N, [a, b, c], b).
N = 2 ;
false.
Look at its source code:
?- listing(nth1).
lists:nth1(A, C, D) :-
integer(A), !,
B is A+ -1,
nth0_det(B, C, D).
lists:nth1(A, B, C) :-
var(A), !,
nth_gen(B, C, 1, A).
true.
?- listing(nth0_det).
lists:nth0_det(0, [A|_], A) :- !.
lists:nth0_det(1, [_, A|_], A) :- !.
lists:nth0_det(2, [_, _, A|_], A) :- !.
lists:nth0_det(3, [_, _, _, A|_], A) :- !.
lists:nth0_det(4, [_, _, _, _, A|_], A) :- !.
lists:nth0_det(5, [_, _, _, _, _, A|_], A) :- !.
lists:nth0_det(A, [_, _, _, _, _, _|C], D) :-
B is A+ -6,
B>=0,
nth0_det(B, C, D).
true.
?- listing(nth_gen).
lists:nth_gen([A|_], A, B, B).
lists:nth_gen([_|B], C, A, E) :-
succ(A, D),
nth_gen(B, C, D, E).
true.

The variable N has not been instantiated to a numeric type when Prolog attempts to evaluate the goals N > 1 and N1 is N - 1 in the recursive clause defining index/3. This causes the instantiation error you are reporting.
I don't know how to solve your problem directly, but I have two suggestions. The first is to use an accumulator, so that the arithmetic operations in the recursive clause can be evaluated:
get(M,Xs,X) :- get(1,M,Xs,X).
get(N,N,[X|_],X).
get(N,M,[_|Xs],X) :-
L is N + 1,
get(L,M,Xs,X).
For instance:
?- index(N,[a,b],X).
N = 1,
X = a ;
N = 2,
X = b ;
false.
The other is to use a natural number type, so that the index can be constructed via unification:
nat(0).
nat(s(N)) :- nat(N).
get(s(0),[X|_],X).
get(s(N),[_|Y],X) :- get(N,Y,X).
For instance,
?- get(N,[a,b],X).
N = s(0),
X = a ;
N = s(s(0)),
X = b ;
false.
Hopefully this was helpful. Perhaps someone more knowledgeable will come along and give a better solution.

Related

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.

How to improve solution for string concatenation?

SWI-Prolog has no list concatenation function, and I have to write it myself. I know the classic solution, but it does not allow access to the head and tail of the second list.
conc([], L, L).
conc([X|L1], L2, [X|L3]):-
conc(L1, L2, L3).
How can I add a program to ask questions to the compiler like this?
?- conc([_, _, _], [L | [_, _, _]], [a,b,c,d,e,f,g,h,i,j,k,l,m]).
I know that I can write the same thing in the form:
?- conc([_, _, _ | L], [_, _, _], [a,b,c,d,e,f,g,h,i,j,k,l,m]).
A query like
conc([_, _, _], [L | [_, _, _]], [a,b,c,d,e,f,g,h,i,j,k,l,m]).
does not make much sense, given you are looking for a list L. After all [L|[_,_,_]] is just equivalent to [L,_,_,_], although the latter is syntactical sugar.
If you are looking for a way to obtain the elements in front of the list, you can just use conc/3 a second time, like:
conc([_,_,_], B, [a,b,c,d,e,f,g,h,i,j,k,l,m]), conc(L, [_,_,_], B).
This gives us the expected result:
?- conc([_,_,_], B, [a,b,c,d,e,f,g,h,i,j,k,l,m]), conc(L, [_,_,_], B).
B = [d, e, f, g, h, i, j, k, l|...],
L = [d, e, f, g, h, i, j] ;
false.
We can make a conc/4 that splits the list in three parts, like:
conc(A, B, C, L) :-
conc(A, D, L),
conc(B, C, D).
and then query like:
?- conc([_,_,_], L, [_,_,_], [a,b,c,d,e,f,g,h,i,j,k,l,m]).
L = [d, e, f, g, h, i, j] ;
false.

How can I reverse list and replace all the specified values with other value?

Imagine that we have a list: [1,2,2,3,4]. The problem can be splitted into two parts:
Reverse the list (we should receive [4,3,2,2,1])
Replace, e.g. all 2 with 100, so we should receive [4,3,100,100,1]
It's easy to reverse the list, here's a working code:
simple_reverse(List, Rev) :-
simple_reverse(List, Rev, []).
simple_reverse([], L, L).
simple_reverse([H|T], L, SoFar) :-
simple_reverse(T, L, [H|SoFar]).
But I have some troubles with replacing the elements. I have tried the following approach:
reverse(a, b, List, Rev) :-
reverse(a, b, List, Rev, []).
reverse(a, b, [], L, L).
reverse(a, b, [H|T], L, SoFar) :-
reverse(a, b, T, L, [H|SoFar]).
reverse(a, b, [a|T], L, SoFar) :-
reverse(a, b, T, L, [b|SoFar]).
What is the problem? By the way, I'm using https://swish.swi-prolog.org/ to run the code.
Instead of just a,b (which are atoms) you need to place variables in reverse/4 and reverse/5 predicates:
reverse(A, B, List, Rev) :-
reverse(A, B, List, Rev, []).
reverse(_, _, [], L, L).
reverse(A, B, [H|T], L, SoFar) :-
dif(A,H), %case where H is not A so we skip it
reverse(A, B, T, L, [H|SoFar]).
reverse(A, B, [A|T], L, SoFar) :-
reverse(A, B, T, L, [B|SoFar]).
You can use foldl in conjonction with library(lambda) :
:- use_module(library(lambda)).
my_reverse(L, A, B, R) :-
foldl([A,B] +\X^Y^Z^(dif(X, A) -> Z = [X |Y]; Z = [B|Y]), L, [],R).
example :
?- my_reverse([1,2,2,3,4], 2, 100, R).
R = [4, 3, 100, 100, 1].

Prolog lists transforming

I want to transform a list in this format:
C=[via(A,B,C,D),via(G,T,H,U),via(J,O,L,P)]
into the following:
F=[(C,D),(H,U),(L,P)]
The letters from F correspond to the letters from C.
It could be something like:
transform([], []).
transform([via(_, _, X, Y)|T)], [(X, Y)|TT) :-
transform(T, TT).
Using library(lambda) it comes down to:
..., maplist(\via(_,_,X,Y)^(X,Y)^true, C, F), ...
several Prologs (like SWI-Prolog I'm using here, in library(apply)) have maplist:
1 ?- [user].
|: transform(via(_,_,C,D),(C,D)).
(ctrl+D here)
true.
2 ?- X = [via(A,B,C,D),via(G,T,H,U),via(J,O,L,P)], maplist(transform,X,Y).
X = [via(A, B, C, D), via(G, T, H, U), via(J, O, L, P)],
Y = [ (C, D), (H, U), (L, P)].

List in Prolog, an unknown substitution?

Just a little question because I do not understand something on a program I have at one of my finals :
p(A, [A | _]).
p(B, [_, _ | C]) :- p(B, C).
q(D, [_, D | _]).
q(E, [_, _ | F]) :- q(E, F).
r(G, H) :- p(G, H).
r(I, J) :- q(I, J).
The question is to make the Tree of research of that with the purpose : r(X, [a,b,c]).
So actually, two possibilities :
r(G, H) :- p(G, H). (With G = X, and H = [a,b,c]).
r(I, J) :- q(I, J). (With I = X, and J = [a, b, c]).
If we take the 1st one, we have : p(X, [a,b,c]).
So we can use the 1st rule : p(A, [A | _]). (With A = X).
But I don't understand why when I launch the SWI-Prolog, a goes into X ...
Thanks !
Satisfying r(X, [a,b,c]) with the first rule goes into satisfying p(X, [a,b,c]).
Your first rule for p/2, p(A,[A|_]), states that a goal p(X,L) is satisfied when L is a list and its first element unifies with X.
In your case that list is [a,b,c] and X is unified with a.

Resources