Arguments are not sufficiently instantiated SWI-Prolog

4k views Asked by At

I trying to write a Prolog script that can create a list of strings that would after a simple procedure result in a given string. My knowledge of Prolog is very limited and I am not sure it is even able to perform this, so please tell me if it's impossible.

I got this so far

replace_word(Old, New, Orig, Replaced) :-
    atomic_list_concat(Split, Old, Orig),
    atomic_list_concat(Split, New, Replaced).

It can perform this operation

10 ?- replace_word('a','e','glava',X).
X = gleve.

But it cannot backtrack it

11 ?- replace_word('a','e',X,'gleve').
ERROR: atomic_list_concat/3: Arguments are not sufficiently instantiated

I can imagine what causes the problem, but is there a way around it?

2

There are 2 answers

0
CapelliC On

replace_word/4 was written that way for efficiency, due to SWI-Prolog first class handling of atoms. To 'reverse' its semantic, I can't see a better way than using an helper predicate:

replace_word(Old, New, Orig, Replaced) :-
    (   var(Orig)
    ->  atomic_list_concat(L, New, Replaced),
        insert_alt(L, [Old, New], L1),
        atomic_list_concat(L1, Orig)
    ;   atomic_list_concat(L, Old, Orig),
        atomic_list_concat(L, New, Replaced)
    ).
insert_alt([A,B|R], Ks, [A,K|T]) :-
    member(K, Ks),
    insert_alt([B|R], Ks, T).
insert_alt([A], _Ks, [A]).

insert_alt/3 it's a bit more general than required, instead of a list (Ks) the pair of Old-New could be used....

test:

?- replace_word('a','e',X,'gleve').
X = glava ;
X = glave ;
X = gleva ;
X = gleve ;
false.
2
Diego Sevilla On

Sure there should be other options in implementing this, and I'm not a Prolog expert, but it seems that atomic_list_concat/3 won't accept two variables as arguments (given what this predicate does it would be tricky, right?), so you can bypass the problem with being given a variable by changing the order of the clauses to "fix" the variables that you know before. For example:

replace_word(Old, New, Orig, Replaced) :-
    (var(Orig), !, atomic_list_concat(Split, New, Replaced),
     atomic_list_concat(Split, Old, Orig)) ;
    (atomic_list_concat(Split, Old, Orig),
     atomic_list_concat(Split, New, Replaced)).

Note that the cut is not necessary, but now you can do what you ask for:

?- replace_word('a','e',X,'gleve').
X = glava.

?- replace_word('a','e','glava',Y).
Y = gleve.