Reversible Predicates and Strings in SWI-Prolog

append/3 is a very powerful predicate. Suppose I want the predicate to work the same, but for SWI-Prolog lines.

The easiest approach I see is to convert these strings to lists using string_codes/2 , then apply append/3 , and then use string_codes/2 backwards. The big problem with this approach is that string_codes/2 does not work unless both variables are unified.

Here is a very ugly solution that I came across that checks which strings are unified to apply string_codes/2 if necessary:

 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 gives 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 easier than this? Suppose I want to create a whole bunch of predicates that work with strings just as they do with lists: I obviously don't want to write everything I did for append/3 for everyone. But I also do not want to work with lines of code, because then I have no way to find out if I am manipulating a regular list or really a line.

+4
source share
4 answers

Just use string_concat / 3. Like ISO atom_concat / 3, it can be used in many modes, including (-, -, +).

+3
source

Since the predicate works on lists, I find it tempting to use DCG. First of all, pay attention to the fact that lines 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 look at the symbols themselves for their codes:

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

This is better. Thinking about the relation that the predicate should describe, I choose a descriptive name, for example list_list_appended / 3. This predicate has one purpose: a dcg rule, let it call list_list // 2, which uses another dcg, call its 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 nothing in the list list([X|Xs]) --> % if there a head element ... [X], % ... it 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 sample 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 can also use this library in combination with set_prolog_flag(double_quotes,chars). to get the result in the desired form. See this answer for more details.

+4
source
Some time ago there was a similar question , I will show my proposal, revised
 :- meta_predicate when_(0). when_(P) :- strip_module(P,_,Q), Q =.. [_|As], or_list(As, Exp), % hurry debugging :-) display(Exp), when(Exp, P). or_list([A], ground(A)) :- !. or_list([A|As], (ground(A);Exp)) :- or_list(As, Exp). append_strings(S1, S2, S3) :- maplist(when_, [string_codes(S1, A), string_codes(S2, B), append(A,B,C), string_codes(S3, C)]). 

If you're interested, I can add a statement to hide the details of the syntax to get something like

 append_strings(S1, S2, S3) -:- string_codes(S1, A), string_codes(S2, B), append(A,B,C), string_codes(S3, C). 
+1
source

This is a more compact definition:

 append_strings(S1, S2, S3):- append_strings1(S1, L1, [1]-[], N1), append_strings1(S2, L2, [1|N1]-N1, N2), append_strings1(S3, L3, [1,1|N2]-N2, N3), (N3\=[_,_|_] ->instantiation_error(append_strings/3); true), append(L1, L2, L3), (ground(S1)->true;string_codes(S1, L1)), (ground(S2)->true;string_codes(S2, L2)), (ground(S3)->true;string_codes(S3, L3)). append_strings1(S, L, G-NG, N):- (ground(S) -> (string_codes(S, L), N=G) ; N=NG). 

It checks whether each argument is grounded and tries to convert it to codes, and then checks whether the third argument is grounded or two others, and generates a creation error if the conditions are not met.

Once added, it converts back to string arguments that are not grounded.

+1
source

All Articles