SWI-Prolog 中的可逆谓词和字符串
Reversible predicates and Strings in SWI-Prolog
append/3
是一个非常强大的谓词。假设我想要一个以相同方式工作但用于 SWI-Prolog 字符串的谓词。
我看到的最简单的方法是使用 string_codes/2
将这些字符串转换为列表,然后应用 append/3
,然后再使用 string_codes/2
。这种方法的大问题是,如果两个变量不统一,string_codes/2
将不起作用。
这是我想出的一个极其丑陋的解决方案,它检查哪些字符串是统一的,以便在需要时应用 string_codes/2
:
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).
这会在以下情况下产生正确的结果:
?- 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.
真的没有比这更简单的方法了吗?假设我想制作一大堆像处理列表一样处理字符串的谓词:我显然不想为所有这些都写下我为 append/3
所做的事情。但我也不想使用代码字符串,因为那样我就无法知道我是在操纵一个普通列表还是一个字符串。
前段时间有个,我把我的提案展示一下,修改后
:- 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)]).
如果你有兴趣,我可以添加一个运算符来隐藏语法细节,得到类似
的东西
append_strings(S1, S2, S3) -:-
string_codes(S1, A), string_codes(S2, B), append(A,B,C), string_codes(S3, C).
这是一个更紧凑的定义:
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).
它检查每个参数是否为基础并尝试转换为代码,然后检查第三个参数是否为基础或其他两个,如果不满足条件则抛出实例化错误。
追加后,它会转换回字符串参数,其中没有地面。
由于谓词在列表上工作,我似乎很想使用 DCG。首先让我们观察一下 Prolog 中的字符串实际上是字符代码列表:
?- X="test".
X = [116,101,115,116]
当然这不是很可读,所以让我们看看字符本身而不是它们的代码:
?- set_prolog_flag(double_quotes,chars).
yes
?- X="test".
X = [t,e,s,t]
这样更好。考虑到谓词应该描述的关系,我选择了一个描述性的名字,比如 list_list_appended/3。这个谓词有一个目标:一个 dcg-rule,我们称之为 list_list//2,它使用另一个 dcg,我们称之为 list//2,来实际编写列表:
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
您的示例查询:
?- 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
作为 SWI 用户,您还可以使用 this library in combination with set_prolog_flag(double_quotes,chars).
to get the output in desired form. Refer to this answer 了解详情。
只需使用string_concat/3。与ISO atom_concat/3一样,它可以在多种模式下使用,包括(-,-,+)。
append/3
是一个非常强大的谓词。假设我想要一个以相同方式工作但用于 SWI-Prolog 字符串的谓词。
我看到的最简单的方法是使用 string_codes/2
将这些字符串转换为列表,然后应用 append/3
,然后再使用 string_codes/2
。这种方法的大问题是,如果两个变量不统一,string_codes/2
将不起作用。
这是我想出的一个极其丑陋的解决方案,它检查哪些字符串是统一的,以便在需要时应用 string_codes/2
:
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).
这会在以下情况下产生正确的结果:
?- 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.
真的没有比这更简单的方法了吗?假设我想制作一大堆像处理列表一样处理字符串的谓词:我显然不想为所有这些都写下我为 append/3
所做的事情。但我也不想使用代码字符串,因为那样我就无法知道我是在操纵一个普通列表还是一个字符串。
前段时间有个
:- 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)]).
如果你有兴趣,我可以添加一个运算符来隐藏语法细节,得到类似
的东西append_strings(S1, S2, S3) -:-
string_codes(S1, A), string_codes(S2, B), append(A,B,C), string_codes(S3, C).
这是一个更紧凑的定义:
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).
它检查每个参数是否为基础并尝试转换为代码,然后检查第三个参数是否为基础或其他两个,如果不满足条件则抛出实例化错误。
追加后,它会转换回字符串参数,其中没有地面。
由于谓词在列表上工作,我似乎很想使用 DCG。首先让我们观察一下 Prolog 中的字符串实际上是字符代码列表:
?- X="test".
X = [116,101,115,116]
当然这不是很可读,所以让我们看看字符本身而不是它们的代码:
?- set_prolog_flag(double_quotes,chars).
yes
?- X="test".
X = [t,e,s,t]
这样更好。考虑到谓词应该描述的关系,我选择了一个描述性的名字,比如 list_list_appended/3。这个谓词有一个目标:一个 dcg-rule,我们称之为 list_list//2,它使用另一个 dcg,我们称之为 list//2,来实际编写列表:
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
您的示例查询:
?- 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
作为 SWI 用户,您还可以使用 this library in combination with set_prolog_flag(double_quotes,chars).
to get the output in desired form. Refer to this answer 了解详情。
只需使用string_concat/3。与ISO atom_concat/3一样,它可以在多种模式下使用,包括(-,-,+)。