这是一个使用 DCG 和 term_expansion 的解决方案,适用于 SWI-Prolog。一、基础:
:- set_prolog_flag(double_quotes, chars).
这确保"foo" 将被解释为三个字符的列表,而不是一些非标准的原子“字符串”对象。
然后,假设有效的pattern0 和pattern1 匹配只是字母列表。由您来填写详细信息。一些基本的 DCG:
letters -->
[].
letters -->
[Letter],
{ char_type(Letter, alpha) },
letters.
pattern0 -->
letters.
pattern1 -->
letters.
例如:
?- phrase(pattern0, Pattern0).
Pattern0 = [] ;
Pattern0 = ['A'] ;
Pattern0 = ['A', 'A'] ;
Pattern0 = ['A', 'A', 'A'] ;
Pattern0 = ['A', 'A', 'A', 'A'] ;
Pattern0 = ['A', 'A', 'A', 'A', 'A'] .
?- phrase(pattern0, "helloworld").
true.
此外,方便的 DCG 仅描述了一个列表:
list([]) -->
[].
list([X | Xs]) -->
[X],
list(Xs).
这似乎作用不大,但一会就派上用场了:
?- phrase(list([a, b, c]), List).
List = [a, b, c].
?- phrase(list(List), [a, b, c]).
List = [a, b, c] ;
false.
现在,您想定义一个像Pattern0.."->"..Pattern1.."|"..Next 这样的复合模式。我建议以不同的方式编写它,即作为子模式列表:[pattern0, "->", pattern1, "|", Next]。这样的列表可能包含三种元素:
- DCG 规则名称
- 文字列表
- 可能绑定到字符列表的变量
然后我们可以编写一个匹配一些复合模式的 DCG:
composite([]) -->
[].
composite([Head | Tail]) -->
{ atom(Head) },
% Assume that this atom is the name of another DCG rule, and execute it.
call(Head),
composite(Tail).
composite([Head | Tail]) -->
list(Head),
composite(Tail).
这表示复合模式仅描述其子模式所描述的任何内容的序列。它只有两个处理子模式的子句:一个用于 DCG 规则名称(由原子表示),一个用于字符列表。变量的大小写由字符列表子句自动处理!
我们可以使用这个定义来匹配一个像"foo->bar|baz" 这样的字符列表和一个复合模式:
?- phrase(composite([pattern0, "->", pattern1, "|", Next]), "foo->bar|baz").
Next = [b, a, z] ;
false.
快完成了!我们可以将其打包到封装模式的定义中:
process_str(Sequence, Next) :-
phrase(composite([pattern0, "->", pattern1, "|", Next]), Sequence).
这样工作:
?- process_str("foo->bar|baz", Next).
Next = [b, a, z] ;
false.
我觉得这已经很不错了。但如果你真的想要一种模式匹配语法,term_expansion 会有所帮助。它的使用(看似)简单:为term_expansion(SomeTermPattern, SomeOtherTerm) 定义一个子句,并且每个匹配SomeTermPattern 的子句定义都将被视为程序员编写了SomeOtherTerm。所以:
term_expansion(
% Replace every definition of this form:
patterned_process_str(Pattern, Next),
% by a replacement like this:
patterned_process_str(Sequence, Next) :-
phrase(composite(Pattern), Sequence)
).
patterned_process_str([pattern0, "->", pattern1, "|", Next], Next).
我们可以查看 Prolog 对 patterned_process_str 源代码的内部表示,以确保它符合预期:
?- listing(patterned_process_str).
patterned_process_str(B, A) :-
phrase(composite([pattern0, [-, >], pattern1, ['|'], A]), B).
变量名称丢失了,但是我们对patterned_process_str 的定义被扩展为我们想要的形式,即我们为上面为process_str 编写的相同形式。这个定义和上面的process_str 完全一样(因为它是等价的):
?- patterned_process_str("foo->bar|baz", Next).
Next = [b, a, z] ;
false.
练习:为.. 提供运算符定义。编写一个谓词pattern_list,在带有.. 的“点模式”和“列表模式”之间进行转换,例如:pattern_list(A..B..C, [A, B, C]) 应该成功。然后,扩展上述term_expansion 规则,使您可以直接使用“点模式”语法编写patterned_process_str。