Prolog DCG实现负规则命中时不解析、否则匹配任意内容
DCG负向匹配排除特定序列实现方案
需求说明
编写DCG规则实现以下逻辑:
- 若字符串开头匹配
bc序列,则解析直接失败,不执行后续解析 - 其余场景下,正常匹配解析当前位置的任意单个字符
现有待补全的代码框架:
% Prolog bc(B, C) --> [B, C], { B = "b", C = "c" }. not_bc(O) --> [O], % 待补全:当前位置不能匹配bc规则 % 目标测试结果 % ?- phrase(not_bc(O), "bcdefg"). % false. % ?- phrase(not_bc(O), "abcdefg"). % O = "a". % ?- phrase(not_bc(O), "wxcybgz") % O = "w". % ?- phrase(not_bc(O), "wxybgz") % O = "w".
以上为问题的简化版本,方案需要支持同构适配更复杂的实际匹配场景。
实现方案
Prolog DCG原生没有提供正则式里的负向前瞻运算符,直接用Prolog内置的否定即失败运算符\+结合DCG的隐式状态传递,就能实现需要的负向断言逻辑,不需要额外依赖。
首先修正原有代码的隐性错误:标准Prolog中双引号字符串是字符码列表,比如"b"等价于单元素列表[98],而DCG语法中[X]是从输入列表里取出单个元素(即单个字符码),原来写的B = "b"是把单个字符码和单元素列表做匹配,永远不会成立,需要先修正bc规则的匹配逻辑。
最终可运行代码如下:
% 匹配开头的bc序列,两种写法二选一即可 % 简洁写法:直接匹配字符串"bc" bc --> "bc". % 保留原带参数写法的修正版本 % bc(B, C) --> [B, C], { B = 0'b, C = 0'c }. % 0'b是Prolog中字符b的字符码语法 % 核心规则:先断言当前位置不匹配bc,再消费单个字符 not_bc(O) --> \+ bc, % 负向断言:当前输入开头不能匹配bc序列 [O]. % 断言通过后,消费当前位置的单个字符作为返回值
如果用带参数的bc(B,C)版本,负向断言部分写成\+ bc(_, _)即可。
效果验证
加载上述代码后运行测试用例,完全符合预期:
- 输入
"bcdefg"时,开头命中bc规则,负向断言失败,整个查询返回false - 输入
"abcdefg"时,开头为字符a,不匹配bc,成功消费第一个字符返回 - 输入
"wxcybgz"、"wxybgz"时,开头均无bc序列,正常返回第一个字符w
如果需要扩展匹配更长的内容(比如匹配所有出现bc之前的全部字符),直接基于not_bc写递归规则即可:
% 匹配到bc就停止,不消费bc本身的内容 parse_until_bc([]) --> bc, !. parse_until_bc([O|Rest]) --> not_bc(O), parse_until_bc(Rest).
这个实现逻辑可以直接迁移到复杂场景,只需要把bc替换成你实际需要排除的目标DCG规则,不需要改动核心的负向匹配逻辑。
内容的提问来源于stack exchange,提问作者eguneys
相关产品推荐
相关产品推荐

