Prolog程序按分号重复匹配问题及代码逻辑错误排查
Prolog规则错误分析与修正
问题背景
定义的program术语如下:
program( [ argument(int, source_account), argument(int, destination_account), argument(int, amount) ], [ change(source_account, amount), change(destination_account, amount) ]).
需求是匹配该术语中source_account和destination_account参数在change语句中的使用情况,生成包含对应problem(change(...))的列表。
当前执行结果
调用interception_between(X, Y, Z)得到:
?- interception_between(X, Y, Z). "Found fact no more body" X = program([argument(_, _A)|_], [change(_A, _)]), Y = [], Z = [problem(change(_A))] ; X = program([argument(_, _A)|_], [change(_A, _)]), Y = [_B], Z = [_B, problem(change(_A))] ;
预期结果
期望捕获两个参数的引用,得到:
X = program([argument(_, _A)|_], [change(_A, _)]), Y = [_B], Z = [problem(change(_B)), problem(change(_A))] ;
额外问题
按下分号后程序会持续生成带更多变量的Y值,无限扩展列表。
用户原始代码
program( [ argument(int, source_account), argument(int, destination_account), argument(int, amount) ], [ change(source_account, amount), change(destination_account, amount) ]). interception_between( program( [argument(_, ARGUMENT_VALUE)|_], [change(ARGUMENT_VALUE, _)|[]]), FACTS, NEW_FACTS1) :- print("Found fact no more body"), append(FACTS, [problem(change(ARGUMENT_VALUE))], NEW_FACTS1). interception_between( program([argument(_, ARGUMENT_VALUE)|ARGUMENT_LIST], [change(ARGUMENT_VALUE, _)|BODY_LIST] ) , FACTS, NEW_FACTS2) :- print("Found fact, calling recursively"), append(FACTS, [problem(change(ARGUMENT_VALUE))], NEW_FACTS1), interception_between( program( ARGUMENT_LIST, BODY_LIST ) , NEW_FACTS1, NEW_FACTS2). interception_between( program( [], []), [], _). interception_between( program( _, []), [], _).
错误原因分析
参数匹配逻辑错位:原始规则仅匹配
argument列表和change列表中位置对应的元素,而非遍历所有change语句找出引用的参数,导致只能捕获一个change引用,无法覆盖两个目标参数。无限生成结果的根源:最后两条规则中第三个参数使用通配符
_,未限制结果列表的生成逻辑,Prolog会不断尝试扩展输入的FACTS列表(即Y参数),从而生成无限多的变量组合。预期结果的逻辑偏差:原始代码将
Y作为输入的FACTS列表,而非自动捕获第二个参数的引用,需要调整规则以遍历所有change语句并匹配对应的argument。
修正方案
方案1:使用findall直接匹配(简洁高效)
program( [ argument(int, source_account), argument(int, destination_account), argument(int, amount) ], [ change(source_account, amount), change(destination_account, amount) ]). % 主规则:空初始FACTS时,生成所有引用argument的problem列表 interception_between(program(Args, Body), [], Problems) :- findall(problem(change(Arg)), (member(argument(_, Arg), Args), member(change(Arg, _), Body)), Problems). % 带初始FACTS的情况:合并初始列表与匹配结果 interception_between(program(Args, Body), Facts, CombinedProblems) :- Facts \= [], findall(problem(change(Arg)), (member(argument(_, Arg), Args), member(change(Arg, _), Body)), Problems), append(Facts, Problems, CombinedProblems).
测试结果:
?- interception_between(X, [], Z). X = program([argument(int, source_account), argument(int, destination_account), argument(int, amount)], [change(source_account, amount), change(destination_account, amount)]), Z = [problem(change(source_account)), problem(change(destination_account))] ; false. ?- interception_between(X, [problem(change(amount))], Z). X = program([argument(int, source_account), argument(int, destination_account), argument(int, amount)], [change(source_account, amount), change(destination_account, amount)]), Z = [problem(change(amount)), problem(change(source_account)), problem(change(destination_account))] ; false.
方案2:递归遍历(保留原始递归思路)
program( [ argument(int, source_account), argument(int, destination_account), argument(int, amount) ], [ change(source_account, amount), change(destination_account, amount) ]). % 基础情况:body为空时,返回输入的FACTS interception_between(program(_, []), Facts, Facts). % 递归处理每个change语句:匹配到argument则添加problem,否则跳过 interception_between(program(Args, [change(Arg, _)|RestBody]), Facts, FinalProblems) :- (member(argument(_, Arg), Args) -> append(Facts, [problem(change(Arg))], TempProblems) ; TempProblems = Facts ), interception_between(program(Args, RestBody), TempProblems, FinalProblems).
测试结果:
?- interception_between(X, [], Z). X = program([argument(int, source_account), argument(int, destination_account), argument(int, amount)], [change(source_account, amount), change(destination_account, amount)]), Z = [problem(change(source_account)), problem(change(destination_account))] ; false.
两种方案都能正确捕获两个参数的引用,且不会生成无限多的结果。
内容的提问来源于stack exchange,提问作者Samuel Squire
相关产品推荐
相关产品推荐

