如何用Prolog基础DCG实现正则式风格的子串捕获并生成字符串?
需求:给Prolog基础DCG添加正则式风格的子串捕获功能
正则表达式可轻松捕获子串,比如字符串"Jaco was an American bassist"匹配PCRE2语法的正则表达式:
(?sm)^([Jj]aco).+(was|is).+?(American|famous).+(dancer|bassist|singer|programmer|dueller)
能捕获到Jaco、was、American、bassist这几个子串。
现有一个DCG可以匹配该字符串,还能生成所有可能的字符串变体,但无法捕获指定子串:
jaco_bassist --> ("J" ; "j"), "aco", space, ("was" ; "is"), space, ("a" ; "an"), space, ("American" ; "famous"), space, ("dancer" ; "bassist" ; "singer" ; "programmer" ; "dueller"). space --> " ".
需要基于Prolog基础DCG(如B-Prolog支持的)实现和正则表达式一致的子串捕获功能,同时保留生成所有可能字符串的能力。用member/2枚举选项的方法在简单场景下可行,但面对复杂结构(比如有48种拼写变体的姓氏kjellerstrand)就存在局限:
kjellerstrand --> "k", ("je" ; "ä"), "ll", ("" ; "er" ; "ar"), ("st" ; "b"), ("" ; "r"), "a", (""; "n"), "d".
实现方案
1. 核心思路:给DCG添加捕获参数
利用DCG的额外参数传递捕获结果,封装专门的capture/2规则,既保留原有匹配/生成逻辑,又能提取目标子串。
% 捕获规则:匹配目标规则Goal,将匹配到的字符串联成Capture原子 capture(Goal, Capture) --> call_dcg(Goal, Chars), { atom_chars(Capture, Chars) }. % 适配B-Prolog的DCG调用逻辑,将规则转换为字符列表 call_dcg(Goal, Chars) --> Goal, { phrase(Goal, Chars) }. % 改造后的jaco_bassist,返回4个捕获项 jaco_bassist(C1, C2, C3, C4) --> capture(("J" ; "j") ++ "aco", C1), % 捕获名字部分 space, capture(("was" ; "is"), C2), % 捕获系动词 space, ("a" ; "an"), space, capture(("American" ; "famous"), C3), % 捕获形容词 space, capture(("dancer" ; "bassist" ; "singer" ; "programmer" ; "dueller"), C4). % 捕获职业 space --> " ".
2. 处理复杂分支结构(如kjellerstrand)
无需拆解原有复杂规则,直接用capture/2包装即可自动捕获所有拼写变体:
% 捕获kjellerstrand的所有拼写形式 capture_kjellerstrand(Capture) --> capture(kjellerstrand, Capture). % 保留原有复杂规则不变 kjellerstrand --> "k", ("je" ; "ä"), "ll", ("" ; "er" ; "ar"), ("st" ; "b"), ("" ; "r"), "a", (""; "n"), "d".
3. 功能验证
- 匹配与捕获:输入字符串时,直接返回对应捕获项:
?- phrase(jaco_bassist(C1,C2,C3,C4), "Jaco was an American bassist"). C1 = "Jaco", C2 = "was", C3 = "American", C4 = "bassist". - 生成所有变体:枚举所有可能的字符串组合,同时得到对应捕获项:
?- phrase(jaco_bassist(C1,C2,C3,C4), String), atom_chars(Str, String). % 输出示例: Str = "Jaco was a famous dancer", C1 = "Jaco", C2 = "was", C3 = "famous", C4 = "dancer" ; Str = "jaco is an American bassist", C1 = "jaco", C2 = "is", C3 = "American", C4 = "bassist" ; % ... 所有其他组合变体
方案优势
- 完全基于基础DCG语法,适配B-Prolog环境;
- 保留DCG原生的生成能力,不会因添加捕获功能丢失枚举变体特性;
- 对复杂分支结构友好,无需修改原有规则逻辑,仅需包装
capture规则即可。
内容的提问来源于stack exchange,提问作者hakank
相关产品推荐
相关产品推荐

