Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reversible predicates and Strings in SWI-Prolog

append/3 is a very powerful predicate. Suppose I want a predicate that works the same way but for SWI-Prolog's strings.

The easiest approach I see is to transform those strings into lists with string_codes/2, then apply append/3, then use string_codes/2 back. The big problem with this approach is that string_codes/2 does not work if both variables are not unified.

Here is an extremely ugly solution I came up with, which checks which strings are unified to apply string_codes/2 when needed:

append_strings(S1, S2, S3) :-
    nonvar(S1),
    nonvar(S2),!,
    string_codes(S1, A),
    string_codes(S2, B),
    append(A,B,C),
    string_codes(S3, C).

append_strings(S1, S2, S3) :-
    nonvar(S1),
    nonvar(S3),!,
    string_codes(S1, A),
    string_codes(S3, C),
    append(A,B,C),
    string_codes(S2, B).

append_strings(S1, S2, S3) :-
    nonvar(S2),
    nonvar(S3),!,
    string_codes(S2, B),
    string_codes(S3, C),
    append(A,B,C),
    string_codes(S1, A).

append_strings(S1, S2, S3) :-
    nonvar(S3),
    string_codes(S3, C),
    append(A,B,C),
    string_codes(S1, A),
    string_codes(S2, B).

This yields the correct results for the following cases:

?- append_strings("test","auie","testauie").
true.

?- append_strings("test",A,"testauie").
A = "auie".

?- append_strings(A,"auie","testauie").
A = "test" ;
false.

?- append_strings(A,B,"testauie").
A = "",
B = "testauie" ;
A = "t",
B = "estauie" ;
A = "te",
B = "stauie" ;
A = "tes",
B = "tauie" ;
A = "test",
B = "auie" ;
A = "testa",
B = "uie" ;
A = "testau",
B = "ie" ;
A = "testaui",
B = "e" ;
A = "testauie",
B = "" ;
false.

Is there really no way to make things simpler than this? Suppose I want to make a whole bunch of predicates that work with strings just like they would with lists: I obviously don't want to have to write what I did for append/3 for all of them. But I also don't want to work with code strings because then I have no way of knowing whether I am manipulating a normal list or really a string.

like image 774
Fatalize Avatar asked Jan 06 '23 06:01

Fatalize


1 Answers

Since the predicate is working on lists, it seems tempting to me to use DCGs. First let's observe that strings in Prolog are really lists of character codes:

   ?- X="test".
X = [116,101,115,116]

Of course this is not very readable, so let's see the characters themselves intead of their codes:

   ?- set_prolog_flag(double_quotes,chars).
yes
   ?- X="test".
X = [t,e,s,t]

That's better. Thinking about the relation the predicate should describe, I opt for a descriptive name like list_list_appended/3. This predicate has one goal: a dcg-rule, let's call it list_list//2, that uses another dcg, let's call it list//2, to actually write the lists:

list_list_appended(L1,L2,L3) :-
    phrase(list_list(L1,L2),L3).   % L3 is L1+L2

list([]) -->                       % if the list is empty ...
    [].                            % ... there's nothing in the list
list([X|Xs]) -->                   % if there's a head element ...
    [X],                           % ... it's in the list
    list(Xs).                      % the tail is also a list

list_list(L1,L2) -->               % the list consists of ...
    list(L1),                      % ... L1 followed by ...
    list(L2).                      % L2

Your example queries:

   ?- list_list_appended("test","auie","testauie").
yes
   ?- list_list_appended(L1,"auie","testauie").
L1 = [t,e,s,t] ? ;
no
   ?- list_list_appended("test",L2,"testauie").
L2 = [a,u,i,e] ? ;
no
   ?- list_list_appended("test","auie",L3).
L3 = [t,e,s,t,a,u,i,e]
   ?- list_list_appended(L1,L2,"testauie").
L1 = [],
L2 = [t,e,s,t,a,u,i,e] ? ;
L1 = [t],
L2 = [e,s,t,a,u,i,e] ? ;
L1 = [t,e],
L2 = [s,t,a,u,i,e] ? ;
L1 = [t,e,s],
L2 = [t,a,u,i,e] ? ;
L1 = [t,e,s,t],
L2 = [a,u,i,e] ? ;
L1 = [t,e,s,t,a],
L2 = [u,i,e] ? ;
L1 = [t,e,s,t,a,u],
L2 = [i,e] ? ;
L1 = [t,e,s,t,a,u,i],
L2 = [e] ? ;
L1 = [t,e,s,t,a,u,i,e],
L2 = [] ? ;
no

As a SWI user you could also use this library in combination with set_prolog_flag(double_quotes,chars). to get the output in desired form. Refer to this answer for details.

like image 137
tas Avatar answered Jan 12 '23 07:01

tas