2010-10-31 13 views
7

J'essaie d'ajouter dynamiquement une règle à la base de connaissances en utilisant SWI-prolog où le corps de la règle est inconnu auparavant.Affirmation de règle dynamique dans SWI-prolog

La règle souhaitée ressemble à ceci:

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

Normalement, vous indiquez simplement

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

mais le problème est que les faits sont décidés lors de l'exécution (le nombre de faits est également inconnu avant l'assertion).

Voilà pourquoi je voudrais savoir s'il est possible d'affirmer une règle selon laquelle le corps est constitué d'une liste de faits tels que [fait (1), fait (2)]

Répondre

4

Nous allons créez une règle newrule(X) :- w,x,y,z(X).
Le corps d'une règle est un tuple, une construction de la forme (w, x, y ...).

pour différentes longueurs de corps, en commençant avec aucun organisme:

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

L'opérateur tuple est la virgule (', '), comme dans ',' (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). 

-

Il y a deux annonces. La première liste résulte de listing() étant appelé en create_a_rule(). La deuxième liste provient de la commande listing() à la dernière ligne source.

?- [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. 
+2

Merci beaucoup pour la solution parfaite et très élaborée! – Tom

+0

@Tom: si la réponse est "parfaite", pourquoi ne pas * l'accepter *? –

+0

Le prédicat ['assert'] (http://www.swi-prolog.org/pldoc/man?predicate=assert/1) dans cette réponse est obsolète dans SWI-Prolog. –

1

modification suggérée sur la liste de Frayser:

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

Ces clauses éviter la nécessité d'une exception si la première variable est une liste vide: il sera tout simplement échouer. Cela signifie également que vous ne serez jamais frappé lors d'un retour en arrière.

Toutefois, vous pouvez néanmoins souhaiter mettre en place la clause d'exception, de sorte que vous puissiez toujours l'insérer dans les cas où une tentative d'unification avec [] a été effectuée. (Il ne le frappera pas en faisant marche arrière, cependant.)