How to concatenate strings in Prolog - string

I'm trying to make an IO-related exercise in Prolog but find it difficult working with strings.
The task is to take an input (an integer 1<=n<=180) and write is as a sum of three terms k*d where k=1,2, or 3 and 1<=d<=20.
For instance:
input: 180
output: triple 20, triple 20, triple 20
input: 96
output: triple 19, double 15, single 9
My problem is that I get error message:
"[some predicate I have tried]: Arguments are not sufficiently
instantiated".
Last thing I tried was a concatenate-predicate I found on another thread at StackOverflow Concatenation of Strings in Prolog. I think it looks nice but I still have the same problem. See my code below.
Before I used string_concat/3 instead.
main :-
repeat,
read(X),
(
X == end_of_file
;
integer(X),
dart_scores(X,N),
write(N),
fail
).
dart_scores(X,N) :-
concatenate([A1,B1,C1],N),
concatenate(["single", A], A1),
concatenate(["double", B], B1),
concatenate(["triple", C], C1),
find_values(X,A,B,C).
find_values(X,A,B,C) :-
X is A+B*2+C*3,
in_domain(A),
in_domain(B),
in_domain(C).
in_domain(D) :-
integer(D),
D>=1,
20>=D.
concatenate(StringList, StringResult) :-
maplist(atom_chars, StringList, Lists),
append(Lists, List),
atom_chars(StringResult, List).

The error you have has nothing to with string concatenation. I have modified your find_values and in_domain predicate to get rid of the error. The problem with your in_domain predicate is that it does not "generate" integers. As for the find_values predicate, you need to unify first A, B, and C with some integers before checking X is A+B*2+C*3, and generate integer for "single", "double", and "triple". Hope this will help you!
main :-
repeat,
read(X),
(
X == end_of_file
;
integer(X),
dart_scores(X,N),
write(N) /*,
fail */
).
dart_scores(R,N) :-
find_values(R,A,B,C,X,Y,Z),
mult(X, A1),
mult(Y, B1),
mult(Z, C1),
concatenate([A1,A], A2),
concatenate([B1,B], B2),
concatenate([C1,C], C2),
concatenate([A2,B2,C2],N).
mult(1, "single").
mult(2, "double").
mult(3, "triple").
find_values(R,A,B,C,X,Y,Z) :-
in_domain(A),
in_domain(B),
in_domain(C),
range(X,1,3),
range(Y,1,3),
range(Z,1,3),
R is A*X+B*Y+C*Z.
in_domain(D) :-
range(D, 1, 20).
range(Low, Low, _).
range(Out, Low, High) :- NewLow is Low+1, NewLow =< High, range(Out, NewLow, High).
concatenate(StringList, StringResult) :-
maplist(atom_chars, StringList, Lists),
append(Lists, List),
atom_chars(StringResult, List).

Related

Replacing substring in second occurrence in prolog

First of all, this is not a homework. I'm studying Computer Sciences in my home, to learn a little more alone.
I'm doing an excercise. It says like this:
Construct a predicate called replaceAtomsString/4 so that given
a string s as the first parameter, a number N as the second parameter,
and a pair of atoms [g, h] (list) as the third parameter, unify in a
fourth parameter the replacement in the Nth apparition of g in s
replacing it by h. Example:
replaceAtomsString (sAbbbsAbbasA, 2, [sA, cc], X) should result in
X = sAbbbccbbasA
So, my first approach was trying to build a list with the string, just like prolog do with every string. After all, i've built this code:
substitute(X, S, T, Y) :-
append(S, Xt, X), % i.e. S is the first part of X, the rest is Xt
!,
substitute(Xt, S, T, Yt),
append(T, Yt, Y).
substitute([Xh|Xt], S, T, [Xh|Yt]) :-
substitute(Xt, S, T, Yt).
But it returns false on every attempt.
Any ideas?
Since you need substantial work to get your code done, here is how to perform the task using the available libraries.
sub_atom/5 it's a rather powerful predicate to handle atoms. Coupled with call_nth/2, the solution is straightforward and more general than what would result coding the loop around N.
replaceAtomsString(S,N,[G,H],X) :-
call_nth(sub_atom(S,Before,_,After,G),N),
sub_atom(S,0,Before,_,Left),
sub_atom(S,_,After,0,Right),
atomic_list_concat([Left,H,Right],X).
Example running your query, but leaving N to be computed:
?- replaceAtomsString(sAbbbsAbbasA, N, [sA, cc], X).
N = 1,
X = ccbbbsAbbasA ;
N = 2,
X = sAbbbccbbasA ;
N = 3,
X = sAbbbsAbbacc ;
false.

Prolog character string list product, can't figure out recursion

one product/3
one_product(+Nonterminal,+Cell,-Product)
this is the closest I've gotten but I can't get the string to multiply with the tail. I've done research on this for a whole week.
conca(String,[H|T],Product) :-
string_concat(String,H,Combined),
append([Combined],T,Product).
one_product(String,Cell,Product) :-
conca(String,Cell,Product).
With that I get
?- one_product("A",["B","C"],What).
What = ["AB", "C"].
A part of another attempt.
Product = String * H.
These are single case attempts.
conca([],[_],Product) :-
Product = [].
conca(String,[],Product) :-
Product = [].
These are examples
?- one_product([],["B"],What).
What = [].
?- one_product("A",["B"],What).
What = ["AB"].
?- one_product("A",["B","C"],What).
What = ["AB", "AC"].
?- one_product("A",[],What).
What = [].
You'll also notice a product with an empty cell is empty.
You don't don't want to use append/3 for something like this. You want a recursive list process:
one_product(_, [], []).
one_product(String, [H|T], [StringH|StringT]) :-
string_concat(String, H, StringH),
one_product(String, T, StringT).
This is a common pattern in Prolog and can be done using maplist/3:
one_product(String, StringList, Product) :-
maplist(string_concat(String), StringList, Product).
This will concatenate (prepend) string String with every string element of list StringList resulting in list Product.
Now, the only issue in the above is with your case:
one_product([],["B"],What).
This case uses a list for the first argument rather than a string. Is that the intent? If so, you can treat it separately:
one_product([], _, []).
one_product(String, StringList, Product) :-
string(String),
maplist(string_concat(String), StringList, Product).

Prolog importing facts from a formatted text file

I have the following input in a text file input.txt
atom1,atom2,atom3
relation(atom1 ,[10,5,2])
relation(atom2 ,[3,10,2])
relation(atom3 ,[6,5,10])
First line includes the list of atoms used in relation predicates in the file and each remaining line represents a relation predicate in order of the first line list.relation(atom1, [x,y,z]) means atom1 has a relation value of 10 with first atom, 5 with the second and 2 with the third
I need to read this file and add represent relation values for each atom seperately.For example , these are the relation values which will be added for atom1 :
assert(relation(atom1, atom1,10)).
assert(relation(atom1, atom2, 5)).
assert(relation(atom1, atom3, 2)).
I have read some prolog io tutorials and seen some recommendations on using DCG but I'm a beginner prolog programmer and having trouble to choose the method for the solving problem. So I'm here to ask help from experienced prolog programmers.
Since you didn't stated what Prolog you're using, here is a snippet written in SWI-Prolog. I attempted to signal non ISO builtins by means of SWI-Prolog docs reference.
parse_input :-
open('input.txt', read, S),
parse_line(S, atoms(Atoms)),
repeat,
( parse_line(S, a_struct(relation(A, L)))
-> store(Atoms, A, L), fail
; true ),
close(S).
:- meta_predicate(parse_line(+, //)).
parse_line(S, Grammar) :-
% see http://www.swi-prolog.org/pldoc/doc_for?object=read_line_to_codes/2
read_line_to_codes(S, L),
L \= end_of_file,
phrase(Grammar, L).
% match any sequence
% note - clauses order is mandatory
star([]) --> [].
star([C|Cs]) --> [C], star(Cs).
% --- DCGs ---
% comma sep atoms
atoms(R) -->
star(S),
( ",",
{atom_codes(A, S), R = [A|As]},
atoms(As)
; {atom_codes(A, S), R = [A]}
).
% parse a struct X,
% but it's far easier to use a builtin :)
% see http://www.swi-prolog.org/pldoc/doc_for?object=atom_to_term/3
a_struct(X, Cs, []) :-
atom_codes(A, Cs),
atom_to_term(A, X, []).
% storage handler
:- dynamic(relation/3).
store(Atoms, A, L) :-
nth1(I, L, W),
nth1(I, Atoms, B),
assertz(relation(A, B, W)).
with the sample input.txt, I get
?- parse_input.
true .
?- listing(relation).
:- dynamic relation/3.
relation(atom1, atom1, 10).
relation(atom1, atom2, 5).
relation(atom1, atom3, 2).
relation(atom2, atom1, 3).
relation(atom2, atom2, 10).
relation(atom2, atom3, 2).
relation(atom3, atom1, 6).
relation(atom3, atom2, 5).
relation(atom3, atom3, 10).
HTH

Prolog convert strings to pair list

I'm learning Prolog and I need idea how to convert a list of strings:
['f(a,45)', 'f(b,13)', 'f(c,12)']
into a list of pairs that looks like this:
[[45,'a'],[13,'b'],[12,'c']]
That's a list of atoms, not strings. Strings in Prolog are usually list of character codes, expressed like
["f(a,45)", "f(b,13)", "f(c,12)"]
Anyway, apply a conversion to each element with a recursive function:
convert([], []).
convert([Atom|Atoms], [Pair|Pairs]) :-
convert_element(Atom, Pair),
convert(Atoms, Pairs).
Instead of recursion you could use maplist/3 in this way:
convert(As, Ps) :- maplist(convert_element, As, Ps).
To convert an element, you need a parser. DCGs are convenient:
convert_element(Atom, [N, A]) :-
atom_codes(Atom, Codes),
phrase(("f(", atomc(Ac), ",", numc(Nc), ")"), Codes, []),
atom_codes(A, Ac),
number_codes(N, Nc).
atomc(A) --> ([C], {is_lowerc(C)}, atomc(Cs), {A = [C|Cs]}) ; {A = []}.
numc(N) --> ([C], {is_numc(C)}, numc(Cs), {N = [C|Cs]}) ; {N = []}.
is_lowerc(C) :- C #>= 0'a, C #=< 0'z.
is_numc(C) :- C #>= 0'0, C #=< 0'9.
test:
?- convert(['f(a,45)', 'f(b,13)', 'f(c,12)'],L).
L = [[45, a], [13, b], [12, c]] .
atomc//1 & numc//1 are expressed in a compact way, but are very simple recursive pattern matching procedures, i.e. atomc//1 could be
atomc([C|Cs]) --> [C], {is_lowerc(C)}, !, atomc(Cs).
atomc([]) --> [].
Alternatively, you can use this code :
convert :-
L = ["f(a,45)", "f(b,13)", "f(c,12)"],
maplist(extract_args,L, LA),
writeln(LA).
extract_args(S, [A1,A2]) :-
string_to_atom(S, A),
term_to_atom(T, A),
T =..[_, A2, A1].
If you have atoms, just use term_to_atom/2 in extract_args/2.

Prolog find all paths Implementation

I've been tasked to implement a version of findall in Prolog without using any Prolog built-ins except for not and cut - so basically in pure Prolog.
I'm trying to search a tree for all direct descendants and return the results in a list
parent(a, b).
parent(b, c).
parent(b, d).
parent(e, d).
What I have so far is:
find(X, L) :- find2(X, [], L).
find2(X, Acc, L) :- parent(Y, X), find2(Y, [Y|Acc], L).
find2(_, Acc, Acc).
What I want to be getting when I enter for example:
find(a,X).
would be:
X = [b, c, d]
(Order not important)
However instead I am getting:
X = [b, c] ;
X = [b, d] ;
X = [b] ;
X = [].
I'm new to Prolog so any help on this would be much appreciated.
Thanks
Besides asserting data as you go, you can also use an extra-logical predicate such as nb_setarg/3. Then once a parent is found, you fail back past nb_setarg and find another parent. All previously found solutions should stay in the term you did nb_setarg on, then after all results are exhausted, the nb_setarg term is the answer. The SWI-Prolog example is good, but its just a counter. Try doing it with a list (or better yet: difference list) that builds as you go.
Take a look at this solution.
Note that this solution uses dynamic predicate named queue in order to cache all solutions until all possibilities are exhausted. Once no more solution exists, implementation retracts all facts and composes the list.
This is of course a bit simplified solution, imagine what would happen if two findall would be active at the same time. It is also a bit fragile on exact semantics of assert and retract if particular prolog implementation
Thanks for you help everyone. I managed to solve it in the end by adding a predicate which checked each item against the current list, and failed if it was already present:
find(X, Loa) :- find(X, [], Loa), !.
find(X, Acc, Loa) :- dec(X, Y), uList(Y, Acc, AccNew), find(X, AccNew, Loa).
find(_, Acc, Acc).
dec(X,Y) :- parent(X,Y).
dec(X,Y) :- parent(X,Z), dec(Z,Y).
uList(X, [], [X]) :- !.
uList(H, [H|_], _) :- !, fail.
uList(X, [H|T], L) :- uList(X, T, Rtn), L = [H|Rtn].

Resources