You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Prolog DCG中不使用cut实现正确的非token匹配?

问题分析

你的核心问题出在not_token的定义上:string(Codes), {\+ phrase(token(_), Codes)}允许匹配任意长度的字符序列(包括空),只要该序列不是完整token。这会导致Prolog回溯时,将token的部分前缀当作非token字符吃掉,进而产生错误解析结果(比如仅匹配单个token或拆分token的情况)。

解决方案

重新定义not_token,使其仅匹配无法作为任何token起始的字符序列,确保不会破坏合法token的完整性。具体做法是逐个检查字符,只吃掉那些绝对不可能启动任何token的字符,直到遇到可能的token起始位置或字符串结束。

修正后的完整代码:

% 主解析器:跳过非token字符 → 匹配token → 跳过非token字符 → 递归解析剩余部分
parser([Token|Rest]) --> not_token, token(Token), not_token, parser(Rest).
parser([]) --> [].

% 定义目标token
token(mul(N1, N2)) --> "mul(", number(N1), ",", number(N2), ")".
token(do) --> "do()".
token(dont) --> "don't()".

% 数字解析辅助规则(确保匹配连续数字)
number(N) --> digits(Ds), { number_codes(N, Ds) }.
digits([D|Ds]) --> digit(D), digits(Ds).
digits([D]) --> digit(D).
digit(D) --> [D], { code_type(D, digit) }.

% 严格的非token匹配:仅吃掉不能启动任何token的字符
not_token -->
    (   [C],
        { \+ starts_token([C|_]) }  % 当前字符无法作为任何token的起始
    ->  [C], not_token
    ;   []).

% 辅助谓词:检查输入是否可以启动某个token(即输入是某个token的前缀)
starts_token(Input) :- phrase(token(_), Input, _).
方案说明
  1. starts_token/1:判断当前位置的字符序列是否是某个token的前缀,确保不会误吞token的起始部分。
  2. not_token递归逻辑:逐个字符检查,只吃掉绝对不属于任何token开头的字符,直到遇到可能的token起始点或字符串结束。
  3. 消除回溯歧义:由于not_token不会破坏token的完整性,解析器只能在合法的token位置匹配,不会产生多余错误解,无需使用cut终止回溯。

测试你的查询:

?- phrase(parser(Tokens), `mul(mul(123,456)dommmmulmul(1,2))`).

现在只会返回唯一正确的解析结果。

内容的提问来源于stack exchange,提问作者Mo...

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 17:53:12