Dynamic rule assertion in SWI-prolog

12,331

Solution 1

We are going to create a rule newrule(X) :- w,x,y,z(X).
The body of a rule is a tuple, a construct in the form (w,x,y...).

For different body lengths, starting with no body:

assert(goal).  
assert(goal:-cond).  
assert(goal:-(cond1,cond2)).

The tuple operator is the comma(`,'), as in ','(a,b) == (a,b).

%%%%
%%%% Name: runtime.pl -- Runtime rule insertion.
%%%%
create_a_rule :- 
    Cond=[w,x,y,z(X)],
    Head=newrule(X),
    list_to_tuple(Cond,Body),
    dynamic(Head),
    assert(Head :- Body),
    listing(Head).

/*
This is a [l,i,s,t], and this is a (t,u,p,l,e).  
Convertng list to tuple:  
[]    -> undefined  
[x]   -> (x) == x  
[x,y] -> (x,y).  
[x,y,z..whatever] = (x,y,z..whatever)  
*/

list_to_tuple([],_) :- 
    ValidDomain='[x|xs]',
    Culprit='[]',
    Formal=domain_error(ValidDomain, Culprit),
    Context=context('list_to_tuple','Cannot create empty tuple!'),
    throw(error(Formal,Context)).

list_to_tuple([X],X).

list_to_tuple([H|T],(H,Rest_Tuple)) :-
    list_to_tuple(T,Rest_Tuple).

:- create_a_rule.
:- listing(newrule).

--

There are two listings. The first listing results from listing() being called in create_a_rule(). The 2nd listing is from the listing() command at the last source line.

?- [runtime].
:- dynamic newrule/1.

newrule(A) :-
    w,
    x,
    y,
    z(A).

:- dynamic newrule/1.

newrule(A) :-
    w,
    x,
    y,
    z(A).

% runtime compiled 0.01 sec, 1,448 bytes
true.

Solution 2

Suggested alteration to frayser's listing:

list_to_tuple([X],X).

list_to_tuple([A,B],(A,B)).

list_to_tuple([A,B|T],(A,B,Rest_Tuple)) :-
    list_to_tuple(T,Rest_Tuple).

These clauses obviate the need for an exception if the first variable is an empty list: it will simply fail. It also means you'll never hit an assert when backtracking.

However, you might nevertheless WANT the exception clause in place, so you could still put it in to catch cases where an attempted unification with [] was made. (It won't hit it when backtracking, though.)

Share:
12,331
Admin
Author by

Admin

Updated on June 08, 2022

Comments

  • Admin
    Admin almost 2 years

    I am trying to add a rule dynamically to the knowledge base using SWI-prolog where the body of the rule is unknown beforehand.

    The desired rule looks something like this:

    rule(a) :- fact(1), fact(2).
    

    Normally you would simply state

    assert((rule(a):-fact(1),fact(2))).
    

    but the problem is that the facts are decided at runtime (the number of facts is also unknown before the assertion).

    That's why I would like to know whether there is a possibility to assert a rule where the body consists of a list of facts such as [fact(1), fact(2)].

  • Admin
    Admin over 13 years
    Thank you so much for the perfect and very elaborated solution!
  • umläute
    umläute over 8 years
    @Tom: if the answer is "perfect", why don't you accept it?
  • Anderson Green
    Anderson Green over 6 years
    The assert predicate in this answer is deprecated in SWI-Prolog.