SWI-Prolog中的可逆谓词和字符串

时间:2016-04-15 12:44:45

标签: string prolog predicate 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所做的所有事情。但我也不想使用代码字符串,因为那时我无法知道我是在操作普通列表还是真正的字符串。

4 个答案:

答案 0 :(得分:4)

由于谓词正在列表中,因此使用DCG似乎很诱人。首先让我们看一下Prolog中的字符串实际上是字符代码列表:

*

当然这不是很易读,所以让我们看看它们代码中的字符本身:

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

那更好。考虑谓词应该描述的关系,我选择了像list_list_appended / 3这样的描述性名称。这个谓词有一个目标:一个dcg规则,我们称之为list_list // 2,它使用另一个dcg,让我们称之为列表// 2,实际写入列表:

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

您的示例查询:

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

作为SWI用户,您还可以将this library ?- 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 结合使用,以获得所需形式的输出。有关详细信息,请参阅this answer

答案 1 :(得分:3)

只需使用string_concat / 3即可。与ISO atom_concat / 3类似,它可用于多种模式,包括( - , - ,+)。

答案 2 :(得分:1)

前一段时间有similar question,我会展示我的提案,修改

:- 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).

答案 3 :(得分:1)

这是一个更紧凑的定义:

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).

它检查每个参数是否为ground并尝试转换为代码,然后检查第三个参数是否为ground或其他两个是,并且如果不满足条件则抛出实例化错误。

在追加后,它会转换回字符串参数,而不是地面。