问题
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.
回答1:
Just use string_concat/3. Like ISO atom_concat/3, it can be used in many modes, including (-,-,+).
回答2:
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.
回答3:
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 ground and tries to convert to codes, then checks if either the third argument is ground or the other two are, and throws an instantiation error if conditions are not met.
After the append it converts back to string arguments which where not ground.
回答4:
there has been a similar question some time ago, 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 are interested, I can add an operator to hide the syntax details, 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).
来源:https://stackoverflow.com/questions/36647522/reversible-predicates-and-strings-in-swi-prolog