How to keep the first result of a function from Pr

2020-04-21 05:25发布

问题:

I need to write a customized function that will be called many times by other fixed functions. In this function, at the first called time, it will return the total number of lines of a file. The second called time of this function, forward, will return the number of lines in small sections of this file. My question is how I keep the first returned result(total number of lines of a file) and use it for the next called times of my function. I need to write or declare any thing only in this function(not in the caller). Something like this:

myFunction(Input, MyResult, FirstResult) :-
   calculateInputFunction(Input, Result),
   !,
   MyResult is Result,
   ... .

The problem is, every time myFunction is called, it receives different Input and returns different MyResult. But I would like to keep the first MyResult to use for next called times of myFunction. How can I do that? Thanks very much for your answer in advance.

myFunction([V1,V2], Result) :-
  reset,
  cached_all(a(V1,V2)),
  use V1, V2 to calculate Result, 
  ...
  reset,
  finishedCode. 

The above is my function, there are other functions that will call myFunction by transfering value of V1, V2 to this function and get Result back.

回答1:

What you need is some form of caching mechanism. That has to use some global resource somehow. The dynamic database is usually used for this purpose. Here is a very simplistic form of it. More sophisticated techniques in that area are known under the notion of tabling.

:- 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.

Usage: Start with reset. Then, wrap Goal as cached_call(Goal). Don't forget to reset when things change!

Here are some explanations:

reset/0 just removes all cached results.

eq/2 is equal up to renaming of variables, provided that the sets of variables of both arguments are disjoint.

cachedgoal_sol/2 is a dynamic predicate. It is used to store solutions (actually: answers) of a particular goal. To this purpose it keeps a copy of the actual goal in the first argument and the actual answer/solution in the second. Note that for one predicate there might be several, different queries. Say: member(X,[a,b,c]) and member(X,[X1,X2,X3]). These queries will be handled and cached independently of each other.

If a goal has to be cached anew, a copy of the term is created to have a "key" for the cache. Then, the goal is executed and each answer is stored - this is done exhaustively. This is particularly interesting for queries that have more than one answer.

Further the goal is protected with catch/3 catching all errors Pat. In this manner, all errors that happen to occur while executing the goal will cause a reset of the cache. This is particularly important for non-terminating queries: cached_call(length(L,N)) would otherwise leave a finite number of solutions in the cache - which would leave the cache in an inconsistent state...

In any case, the first clause always fails. So it is here only for the side effect of updating the cache.

The second clause now uses the cache. Note that it is not possible to XGoal = Goal "further to the left", because eq/2 has to ensure that we are using only the results of the very same query.

As I said already: This is a very naive way of doing this, but at least it is simple and relatively robust.


As to your original program. You can now write:

..., cached_call(calculateInputFunction(Input, Result)), ...

every time you need that value.