Prolog是否会优化相同参数下对同一事实的多次重复调用?
Prolog对重复谓词调用的优化规则
你给出的规则定义如下:
expensive(X, Y) :- % ... . cheap(X, Y) :- expensive(X, Y), % ... cheap2(X, Y) :- expensive(X, Y), % ...
对应的查询规则:
perft(X, Y) :- expensive(X, Y), cheap(X, Y) , \+ cheap2(X, Y).
- 标准Prolog的默认SLD解析执行模型不会自动为相同参数的同一谓词调用做隐式结果缓存。按照上述写法执行
perft(X, Y)时,expensive(X, Y)会被重复调用:- 首先执行
perft规则体最左侧的expensive(X, Y),枚举所有满足条件的X、Y变量绑定 - 对每一组得到的X、Y绑定,进入
cheap(X, Y)调用时,会重新执行cheap规则体内的expensive(X, Y)校验,不会复用第一步已经得到的校验结果 - 当
cheap(X, Y)校验通过后,执行\+ cheap2(X, Y)否定判断时,进入cheap2规则体还会第三次触发expensive(X, Y)校验,同样不会复用前两次的调用结果
- 首先执行
- 极少数Prolog实现的JIT编译模块可能会做有限的公共子表达式消除优化,但这类优化限制极多:仅对无副作用、无剪枝、参数在调用间隔完全没有变化的纯谓词生效,且属于实现的可选特性,完全不能作为通用逻辑的依赖。绝大多数常规Prolog环境的默认解释执行模式,都不会做这类优化。
- 如果
expensive/2的执行开销较高,需要开发者手动优化避免重复调用,常见的可靠方案有三种:- 调整规则结构,抽离公共前提。把
cheap、cheap2规则中重复的expensive(X, Y)前提移到外层调用处,改写cheap、cheap2的规则体,仅保留各自除expensive之外的判断逻辑,这样perft中单次执行expensive(X, Y)通过后,后续cheap、cheap2的判断就不会重复触发该谓词 - 使用制表(Tabling,又称记忆化)扩展。目前主流Prolog实现(SWI-Prolog、XSB、YAP等)均支持Tabling特性,只需在定义
expensive/2前添加声明:- table expensive/2.,Prolog就会自动缓存该谓词在不同参数下的执行结果,后续相同参数的调用会直接返回缓存值,不会重复执行谓词内部逻辑 - 显式传递中间结果。如果
expensive/2执行时会计算生成额外中间值,可以将中间值作为额外参数绑定到变量上,后续逻辑直接使用绑定完成的变量即可,避免重复计算。
- 调整规则结构,抽离公共前提。把
注意:如果谓词存在副作用(包含IO操作、动态数据库修改、显式剪枝
!等),自动优化或者盲目的缓存可能会破坏程序的预期执行语义,这种场景下手动调整规则结构是最稳妥的优化方式。
内容的提问来源于stack exchange,提问作者eguneys
相关产品推荐
相关产品推荐

