`(ab)*` 的两个 DCG 规则。可以是一个吗?

Two DCG rules for `(ab)*`. Can it be one?

我想生成或测试符合 Perl 正则表达式的字符串 (ab)*

下面的代码运行良好:

生成

?- phrase_acceptable(Text,6).
Text = abababababab ;
false.

测试(或压缩?)

?- phrase_acceptable("ababab",N).
[97,98,97,98,97,98]
N = 3 

列举可能性

?- phrase_acceptable(T,N).
T = '',
N = 0 ;
T = ab,
N = 1 ;
T = abab,
N = 2 ;
T = ababab,
N = 3 
...

然而,这需要两个子句acceptable//1,根据N是否新鲜来选择。这可以避免吗?使用 CLP(FD) 没有帮助,因为在任何情况下都必须检查 N>=0 以避免无限下降。

ff(X) :- var(X).    % "freshvar(X)" using 2 letters, which is less annoying
bb(X) :- nonvar(X). % "notfreshvar(X)" using 2 letters, which is less annoying

acceptable(0) --> [].
acceptable(N) --> { bb(N), N>0, succ(Nm,N) }, `ab`, acceptable(Nm).  
acceptable(N) --> { ff(N) }, `ab`, acceptable(Nm), { succ(Nm,N) }. 

phrase_acceptable(Text,N) :-
   bb(Text),!,
   atom_codes(Text,Codes), 
   writeln(Codes),
   phrase(acceptable(N),Codes,[]).
   
phrase_acceptable(Text,N) :- 
   ff(Text),!,
   phrase(acceptable(N),Codes,[]),
   atom_codes(Text,Codes).

这个怎么样:

:- use_module(library(clpfd)).

acceptable(0) --> [].
acceptable(N1) -->  `ab`, { N #= N1-1, N #>= 0 }, acceptable(N).

phrase_acceptable(Text, N):-
  (nonvar(Text) -> atom_codes(Text, Codes) ; true),
  N #>= 0,
  phrase(acceptable(N), Codes, []),
  (var(Text) -> atom_codes(Text, Codes) ; true).

测试用例:

?- phrase_acceptable(ababab,N).
N = 3 ;
false.

?- phrase_acceptable(Text,3).
Text = ababab ;
false.

?- phrase_acceptable(Text,N).
Text = '',
N = 0 ;
Text = ab,
N = 1 ;
Text = abab,
N = 2 ;
Text = ababab,
N = 3 .