How to improve solution for string concatenation? - prolog

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.

Related

Encoding program in prolog

I am currently working on an encoding program in prolog.
In the first place I want to cut a word in pieces
For example: friends should look like:
[[F,R,I][R,I,E][I,E,N][E,N,D][N,D,S]]
For the moment I have something like this but can't understand why is the program not working when I try on a word.
It is always answering false.
couper([X1,X2,X3|L],[L1|ResQ]):-
L1 = [X1,X2,X3],
couper([X2,X3|L],ResQ).
couper([_,_|[]] , []).
couper([] , []).
couper([_|[]] , []).
Your program seems to work:
?- atom_chars(alpha,L),couper(L,Triplets).
L = [a, l, p, h, a],
Triplets = [[a, l, p], [l, p, h], [p, h, a]] ;
Although it can be written easier:
couper([X1,X2,X3|L],[[X1,X2,X3]|ResQ]):-
couper([X2,X3|L],ResQ).
couper([_,_] , []). % Only two chars left or only two to begin with
couper([_] , []). % Only one char to begin with
couper([] , []). % No chars to begin with
You can also try:
?- findall([A,B,C], append(_,[A,B,C|_],[f,r,i,e,n,d,s]), T).
T = [[f, r, i], [r, i, e], [i, e, n], [e, n, d], [n, d, s]].
As a rule:
couper(L, T) :-
findall([A,B,C], append(_, [A,B,C|_], L), T).
Examples:
?- couper([f,r,i,e,n,d,s], T).
T = [[f, r, i], [r, i, e], [i, e, n], [e, n, d], [n, d, s]].
?- couper([e,x,a,m,p,l,e,s], T).
T = [[e, x, a], [x, a, m], [a, m, p], [m, p, l], [p, l, e], [l, e, s]].
?- couper([t,w,o], T).
T = [[t, w, o]].
?- couper([t,o], T).
T = [].
NOTE In Prolog, uppercase letters are variables. Thus, the predicate must be called with a list of lowercase letters (perhaps, this is the cause of your problem).

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

Need to re-structure prolog code

I have the following code:
edge(a,b,5).
edge(b,a,5).
edge(b,c,3).
edge(c,a,2).
edge(c,d,4).
edge(d,b,6).
edge(c,f,4).
edge(f,c,4).
edge(e,c,5).
edge(f,e,7).
edge(g,a,3).
edge(d,g,8).
edge(e,g,2).
edge(f,h,3).
edge(h,i,2).
edge(i,j,3).
edge(j,h,4).
edge(d,h,1).
edge(j,f,6).
%edge(l,k,-1).
%edge(k,l,4).
%edge(a,z,-2).
vertex(a).
vertex(b).
vertex(c).
vertex(d).
vertex(e).
vertex(f).
vertex(g).
vertex(h).
vertex(i).
vertex(j).
member(A, [A|_]) :- !.
member(A, [_|Y]) :- member(A, Y).
path(X,Y) :- pathHelper(X,Y,[],0).
pathHelper(X,X,L, W) :- write([X|L]), write(W).
pathHelper(X,Y,L, W) :- edge(X,Z,C),\+member(Z,L),F is W+C,pathHelper(Z,Y,[X|L], F).
I was wondering how to turn path into a function with 4 arguments so when called like path(a, h, L, W) gives
L = [a, b, c, d, h],
W = 13 ;
L = [a, b, c, f, h],
W = 15 ;
Try to look into the concept of accumulator(they call it accumulator in prolog,Extra argument for result) it's interesting and btw it's not much work:
path(X,Y,Distance,Result):-
pathHelper(X,Y,[],0,Distance,Result).
pathHelper(X,X,L,W,W,P):-
reverse([X|L],P).
pathHelper(X,Y,L, W,Distance,Result):-
edge(X,Z,C),
\+member(Z,L),
F is W+C,
pathHelper(Z,Y,[X|L], F,Distance,Result).

Prolog creating a list of sets from ith elements of lists

I have list structure
L=[[a,b,c,d],[a,f,c,h]]
Length of L can be greater than 2.I want to unite the elements of list so that L or a NewL become
L=[a,[b,f],c,[d-h]]
This is probably what you want:
unite([[],[]], []).
unite([[X|Ls], [X|Rs]], [X|Rest]) :- unite([Ls, Rs], Rest).
unite([[L|Ls], [R|Rs]], [[L,R]|Rest]) :- L \= R, unite([Ls, Rs], Rest).
However, I agree with #false because this is a strange API and there are a lot of unhandled edge cases.
What you're requiring is an aggregation schema. I think I got it:
unite(Ls, [E|Es]) :-
aggreg(Ls, E, Ns),
unite(Ns, Es).
unite(_, []).
aggreg(L, E, LLs) :-
maplist(first, L, Fs, LLs),
setof(X, member(X, Fs), S),
( [E] = S -> true ; E = S ).
first([E|Es], E, Es).
yields
?- L=[[a,b,c,d],[a,f,c,h],[a,f,c,g]],unite(L,U).
L = [[a, b, c, d], [a, f, c, h], [a, f, c, g]],
U = [a, [b, f], c, [d, g, h]] ;
L = [[a, b, c, d], [a, f, c, h], [a, f, c, g]],
U = [a, [b, f], c] .
I think that a cut after the first solution would be well placed (use once/1 for that).
Note that the schema it's rather general: just substitute in setof/3 some more applicative task (if any) than unification (you could call into your DB, for instance).

How to make my relation work

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.

Resources