prologfactorialmemoizationprolog-tabling

Prolog Database Recording


I have this factorial predicate.

fact(0, 1).
fact(N, F) :- N > 0, 
        N1 is N-1,
        fact(N1, F1),
        F is F1 * N.

How do I change this predicate such that every time a query is issued, the result of the calculation is stored in the database? New queries should use then the stored results if available.


Solution

  • What you are looking for is generally called memoization, but is referred to as tabling in the context of Prolog. Conveniently, there is a library for this called tabling for SWI Prolog.

    :- use_module(library(tabling)).
    :- table fact/2.
    
    fact(0, 1).
    fact(N, F) :- N > 0,
                  N1 is N-1,
                  fact(N1, F1),
                  F is F1 * N.
    

    You can verify that the memoization works by running trace before your calls to fact. Note that the two :- lines are called directives and are run by the compiler, so running them in a repl will not work.

    Edit

    If you don't want to use the tabling library, you can accomplish this with asserta. This will insert a fact at the top of the database, as if you had entered it into the file yourself.

    fact(0, 1).
    fact(N, F) :-
                  N > 0,
                  N1 is N-1,
                  fact(N1, F1),
                  F is F1 * N,
                  asserta(fact(N, F)).
    

    Prolog will see the new predicate first and will use it instead of recomputing your factorial. Once again, you can check this by tracing.