您需要的是某种形式的缓存机制。这必须以某种方式使用一些全球资源。动态数据库通常用于此目的
目的。这是它的一个非常简单的形式。该领域中更复杂的技术在表格概念下是已知的。
:- dynamic(cachedgoal_sol/2).
reset :-
retractall(cachedgoal_sol(_,_)).
eq(A, B) :-
subsumes_term(A, B),
subsumes_term(B, A).
cached_call(Goal) :-
\+ ( cachedgoal_sol(Skel,_), eq(Skel, Goal) ), % No fitting Goal was cached
copy_term(Goal, Skel),
catch(
( Goal,
assertz(cachedgoal_sol(Skel,Goal)),
fail
),
Pat,
(reset, throw(Pat))).
cached_call(Goal) :-
cachedgoal_sol(Skel,XGoal),
eq(Skel, Goal),
XGoal = Goal.
用法:开始于reset.
然后,包起来Goal
as cached_call(Goal)
。当事情发生变化时,不要忘记重置!
以下是一些解释:
reset/0
只是删除所有缓存的结果。
eq/2
等于变量重命名,前提是两个参数的变量集不相交。
cachedgoal_sol/2
是一个动态谓词。它用于存储特定目标的解决方案(实际上:答案)。为此,它在第一个参数中保留实际目标的副本,在第二个参数中保留实际答案/解决方案的副本。请注意,对于一个谓词可能有多个不同的查询。说:member(X,[a,b,c])
and member(X,[X1,X2,X3])
。这些查询将彼此独立地处理和缓存。
如果必须重新缓存某个目标,则会创建该术语的副本以拥有缓存的“密钥”。然后,执行目标并存储每个答案 - 这是彻底完成的。这对于具有多个答案的查询尤其有趣。
此外,目标受到保护catch/3
捕获所有错误Pat
。这样,在执行目标时发生的所有错误都会导致reset
的缓存。这对于非终止查询尤其重要:cached_call(length(L,N))
否则会在缓存中留下有限数量的解决方案 - 这将使缓存处于不一致的状态......
无论如何,第一条总是失败。所以这里只是为了更新缓存的副作用。
第二个子句现在使用缓存。请注意,这是不可能的XGoal = Goal
“再向左”,因为eq/2
必须确保我们仅使用同一查询的结果。
正如我已经说过的:这是一种非常幼稚的方法,但至少它很简单并且相对健壮。
至于你原来的程序。你现在可以写:
..., cached_call(calculateInputFunction(Input, Result)), ...
每次你需要这个值的时候。