8. Lancement d'un but Prolog par un programme C. PrologIA HERITAGE II+
A!ociation
Prolog
HERITAGE
8. Lancement d'un but
Prolog par un programme C
8.1. Principes de base
8.2. Initialisation de Prolog
8.3. Empilement d'un but Prolog
8.4. P r o g r a m m a t i o n
8.5. Méthode simple d'appel d'un but Prolog
8.6. Autres fonctions
Ce chapitre suppose une bonne connaissance du chapitre 7, les lecteurs non expérimentés peuvent sauter ce chapitre en première lecture.
La machine Prolog peut être utilisée comme un sous-programme d'un programme quelconque. Pour simplifier la compréhension de sa manipulation depuis un langage procédural, on peut présenter Prolog comme une machine à états (ou points d'arrêt): un jeu de procédures permet de faire transiter la machine Prolog de l'état courant vers un nouvel état.
Hormis aux points d'arrêt de la machine Prolog, un programme C ne peut être appelé que par une règle prédéfinie. Celle-ci peut empiler un nouveau but, ou provoquer une erreur.
A un point d'arrêt de la machine Prolog, un programme C peut: empiler un nouveau but, réactiver la machine jusqu'au prochain point d'arrêt pour avoir une autre solution, ou provoquer l'abandon du but en cours, avec retour à l'état précédent.
8 . 1 .
Principes de base
L' «état normal», pour une machine Prolog, consiste évidemment en l'exécution de la boucle d'effacement d'une suite de buts (cf. chapitre 2 de ce manuel); cette boucle est appelée parfois horloge Prolog. Lorsqu'elle se trouve dans cette situation, nous dirons que la machine Prolog est active.
Quand la machine Prolog s'active, elle possède déjà une suite initiale de buts à effacer; le processus d'effacement (choix d'une règle, unification de la tête, etc…) dure tant que la suite de buts courante n'est pas vide; quand cette suite est vide, la machine tombe dans un point d'arrêt.
© PrologIA
U 3 - 2
Manuel d'Utilisation Windows
Dans ce paragraphe on définit les divers états dans lesquels la machine Prolog peut se trouver lorsqu'elle n'est pas active, tels que les voit le programmeur C (ou Pascal, ou autre…); ces états seront appelés des points d'arrêt. On introduit aussi les procédures qui font passer la machine d'un point d'arrêt à un autre.
A un point d'arrêt, les états possibles de la machine sont :
0.
Machine non initialisée. Ceci n'est pas vraiment un état de la machine, mais la situation dans laquelle on se trouve avant l'initialisation de celle-ci (allocation de l'espace, etc…). La procédure ProStart(…), appelée une seule fois par session, fait passer dans l'état suivant :
1.
Rien a exécuter (
NO_GOAL
). La machine est initialisée, mais la suite de buts à
effacer est vide.
La machine vient aussi dans cet état par son fonctionnement normal, lorsqu'il n'y a plus aucune manière possible d'effacer la suite de buts courante, c'est-àdire lorsque toutes les solutions du problème courant ont été précédemment obtenues. Dans cet état, toute activation de la machine (par next_solution() ) la ramène immédiatement dans le même état NO_GOAL.
2.
But prêt à être exécuté. Ceci est un étape préalable dans l'activation de la machine : la procédure new_goal() vient d'installer la suite de buts
exec(_b, _x); le programme appelant doit maintenant, à l'aide des routines standard (put_integer, put_term, etc…), unifier _b avec le but ou la liste de buts à effacer.
3.
En exécution Prolog, dans une règle prédéfinie. Pendant l'effacement d'une règle externe, le programme C (ou Pascal, etc…) en question obtient le contrôle de l'exécution et la machine Prolog «paraît» à l'arrêt; cependant, cette situation diffère des états précédents dans le fait qu'il ne correspond pas à un point d'arrêt : la machine Prolog n'est pas naturellement arrêtée au début ou a la fin d'un effacement, mais au contraire elle se trouve en plein milieu d'un effacement, en attente de la terminaison d'une procédure externe.
4.
Solution trouvée (
SOLUTION_EXISTS
). C'est le point d'arrêt le plus fréquent : la suite de buts courante vient d'être entièrement effacée. Le programme appelant peut récupérer les solutions à travers l'argument _x de l'appel exec(_b,_x).
La machine Prolog a mémorisé tous les points de choix de l'effacement en cours. A partir de cet état, on peut appeler la procédure
next_solution() pour relancer la machine afin d'obtenir les autres solutions.
La séquence d'états la plus fréquente sera donc:
NO_GOAL ! «But prêt» ! SOLUTION_EXISTS !!…
… !!SOLUTION_EXISTS ! NO_GOAL
5.
Erreur rencontrée (
ERROR
). C'est l'état dans lequel la machine est mise lorsqu'une erreur a fait avorter son fonctionnement normal. Une activation
(par next_solution() ) la fera passer alors dans l'état NO_GOAL.
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 3
Dans n'importe lequel des états 1 à 4 ci-dessus, la procédure new_goal() peut être appelée pour installer un nouveau problème par-dessus le problème courant, lequel est conservé exactement dans l'état où il se trouve afin de pouvoir ultérieurement continuer à obtenir ses solutions.
De la même manière, dans les états 2 à 5 on peut appeler la procédure kill_goal(), qui provoque l'abandon du problème en cours et le retour au problème précédent, qui se retrouve exactement dans l'état où il était quand on a appelé new_goal().
Les fonctions permettant d'activer la machine Prolog (i.e. de la faire transiter vers un autre état) sont:
ProStart(..)
Fonction permettant d'initialiser la machine Prolog. Cette fonction doit être appelée une fois, avant tout autre appel concernant Prolog. Initialise la machine dans l'état 1.
ProFinal(..)
Fonction permettant de terminer et libérer la machine et l'environnement
Prolog. Elle a un argument de type entier qui spécifie le status de terminaison de Prolog.
new_goal()
Mémorise l'état courant de la machine, et fait passer dans l'état 2 (but prêt à
être exécuté). L'état 2 est initialisé avec l'appel exec(_b,_s) dont les variables doivent être instanciées par le programme appelant APRES l'appel de
new_goal.
Cette procédure permet d'empiler des activations de buts _b jusqu'à une profondeur limitée seulement par la taille des piles de Prolog.
kill_goal()
Revient à l'état exact dans lequel se trouvait la machine avant le dernier appel de new_goal. new_goal et kill_goal fonctionnent comme des parenthèses encadrant l'effacement d'un nouveau but Prolog.
next_solution()
Fonction d'activation de la machine, la faisant transiter vers l'état d'arrêt suivant
(1, 4, ou 5). La fonction a pour valeur le nouvel état de la machine Prolog:
SOLUTION_EXISTS (0), NO_GOAL (-1), ERROR (>0).
© PrologIA
U 3 - 4
Manuel d'Utilisation Windows
Pour tout état Prolog différent de l'état non initialisé, le diagramme suivant définit les transitions possibles:
A!ociation
Prolog
HERITAGE
8 . 2 .
Initialisation et terminaison de Prolog
Au démarrage d'une application, Prolog se trouve dans un état non initialisé. La fonction ProStart permet d'initialiser Prolog avec une configuration définie dans la chaîne de caractères passée en paramètre, chaîne représentant les options de lancement de Prolog (taille des piles, fichier d'entrée,..). La fonction retourne 0 si tout c'est bien passé, un nombre non nul positif, correspondant au numéro de l'erreur, si une erreur s'est produite et l'initialisation ne s'est pas faite.
L'appel de toute autre procédure que ProStart dans l'état non initialisé est illégal.
Les types de la fonction et de son argument doivent être comme ci décrit: int ProStart(params)
© PrologIA
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 5 char *params;
Lorsque l'utilisation de Prolog est terminée, la fonction ProFinal permet de libérer les structures et terminer Prolog. La fonction n'a pas de valeur de retour, elle a un argument qui spécifie le status de terminaison de Prolog, en fonction duquel
ProFinal imprime un message. Ce message doit être défini dans le fichier d'erreurs de Prolog. Le status doit valoir 0 pour une terminaison normale, un nombre strictement positif pour un status d'erreur. Ce status d'erreur pourra être, par exemple, l'erreur retournée par une des fonctions suivantes new_goal, next_solution ou kill_goal.
Après l'exécution de ProFinal, Prolog se trouve à nouveau dans un état non initialisé.
Les types de la fonction et de son argument doivent être comme ci décrit:
ProFinal(status) int status;
Voici un exemple d'utilisation : mon_initialisation_prolog()
{ int err; if ((err = ProStart("-c 400 -f cM prolog.po")) == 0) else fprintf(stderr,"initialisation reussie");
{fprintf(stderr,"initialisation echouee!"); exit(err);}
} ma_terminaison_prolog(condition) int condition;
{ if (mon_erreur(condition))
} else
{ mon_message_erreur(condition);
ProFinal(0);
ProFinal(condition);
}
L'utilisation de la machine Prolog dans une application est délimitée par l'appel aux fonctions ProStart et ProFinal.
8 . 3 .
Empilement d'un but Prolog
L'empilement d'un but _b se fait à travers l'appel d'une règle relais exec(_b,_s), qui garantit la conservation du but en cours et l'arrêt dans un état précis. Cette règle a deux arguments: le premier est un terme représentant le but _b à effacer, le deuxième est le terme qui sera obtenu en solution (c'est en général la liste des variables intéressantes du but _b).
© PrologIA
U 3 - 6
Manuel d'Utilisation Windows
Lorsqu'on a appelé new_goal, l'appel exec(_b,_s) est prêt à être exécuté. Il suffit donc d'utiliser les routines standard pour instancier les arguments (put_term(1,..) pour instancier _b, et put_term(2,..) pour définir les variables correspondant à la solution intéressante).
L'appel de la fonction next_solution provoque la transition de la machine Prolog vers un des états 1,4 ou 5. La valeur retournée par la fonction est l'état atteint:
NO_GOAL (-1), ERROR (>0), ou SOLUTION_EXISTS (0).
La règle relais est l'équivalent de: exec(_b,_s) -> block(_e,_b) condition(_e,_k) point_arret1(_k,_s) fail ; exec(_b,_s) -> point_arret2(NO_GOAL,nil); condition(_e,SOLUTION_EXISTS) -> free(_e) !; condition(_e,_e) -> integer(_e) !; condition(_e,BLOCK_EXIT_WITHOUT_BLOCK) ->; point_arret1(K,S) -> stop; point_arret2(K,S)-> stop point_arret2(NO_GOAL,[]);
Conceptuellement, tout se passe comme si l'utilisateur se trouvait dans la règle
point_arret: de par la manière dont est écrit exec, la valeur du 1er argument (obtenu par get_integer(1,..)) contient également l'état atteint. Si cet état est
SOLUTION_EXISTS, le deuxième argument (obtenu avec get_term(2,..)) contient le terme définissant la solution intéressante.
A!ociation
Prolog
HERITAGE
8 . 4 .
Programmation
Le fichier proext.h contient les déclarations des procédures et des structures d'interface. Il faut mettre un #include de ce fichier en tête des modules utilisant ces procédures.
Voici un exemple de programme C imprimant toutes les solutions de enum(i,-2,8), que l'on peut écrire tel quel dans le module C d'interface utilisateur : prouser.c, à la place de la déclaration EXTERNAL_DESCRIPTOR qui s'y trouve.
© PrologIA
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 7
#define MAX_TAB 10 exemple()
{ int err; long i;
P_SYMBOL pro_symbol(); char tags[MAX_TAB]; int vals[MAX_TAB]; int n = 1;
/* initialisation du but */
new_goal(); tags[0] = 'T'; vals[0] = 1; tags[1] = 'I'; vals[1] = 4; tags[2] = 'N'; vals[2] = pro_symbol("sys:enum"); tags[3] = 'V'; vals[3] = 0; tags[4] = 'I'; vals[4] = -2;
/* i */ tags[5] = 'I'; vals[5] = 8;
put_term(1, MAX_TAB,0,0, tags,vals, 0,0, 5, &err); if (err) goto error;
/* creation solution interessante */ tags[0] = 'V'; vals[0] = 0;
put_term(2, MAX_TAB,0,0, tags,vals, 0,0, 0, &err); if (err) goto error;
/* impression des solutions */
while ((err = next_solution()) == SOLUTION_EXISTS)
{
get_integer(2, &i, &err); /*valeur de i*/ if (err) goto error; printf("solution %d : %ld\n", n++,i);
} if (err > 0)
goto error;
kill_goal();
return 0; error: printf("erreur %d a la %d ieme etape\n",err,n); kill_goal(); return err;
}
EXTERNAL_DESCRIPTOR prouser_desc[] =
{{":exemple", C_FUNCTION, 0, (POINTER)exemple},
{0,0,0,0}};
Vous pouvez trouver un exemple d'utilisation de Prolog comme un sous programme, dans le fichier princip.c qui est le module principal de Prolog.
8 . 5 .
Méthode simple d'appel d'un but Prolog
Une autre méthode, plus simple à utiliser, permet également depuis un langage externe, d'installer un but Prolog et d'en récupérer les solutions.
© PrologIA
U 3 - 8
Manuel d'Utilisation Windows
Elle est plus simple pour installer un but, grâce à la fonction new_pattern,
équivalente à l'ensemble new_goal, put_term du but et put_term du résultat. Elle est moins efficace sur ce point là car elle utilise l'analyseur Prolog.
Elle est plus simple pour extraire des valeurs de la solution, grâce à la fonction
get_formats, équivalente à l'ensemble get_term et traitement des différents tableaux.
Le principe de cette méthode est de spécifier au moment de l'installation du but, la structure des arguments et les valeurs qui y sont intéressantes, de façon à simplement les nommer à l'obtention d'une solution, plutôt que faire une recherche algorithmique des sous_termes voulus de la solution.
Les arguments du but appelé peuvent être un terme quelconque (sauf infini) et les valeurs à extraire sont des termes simples tels que entier, réel, double, identificateur ou chaîne.
8 . 5 . 1 .
D e s c r i p t i o n
int new_pattern(char * but)
est une fonction avec en argument unique: une chaîne de caractères représentant un terme Prolog qui est le but à effacer, et où les résultats intéressants sont représentés par des formats. La fonction retourne 0 si tout c'est bien passé, un numéro d'erreur si une erreur s'est produite.
Les différents formats acceptés sont:
%d
pour les entiers
%s pour les chaînes de caractères
%f
pour les réels simples
%F
pour les réels doubles
%i
pour les identificateurs sous forme d'entier
%I
pour les identificateurs sous forme de chaîne de caractères
Voici un exemple d'appels de new_pattern: erreur = new_pattern("sys:enum(%d,-2,10)"); erreur = new_pattern(":essai(<%i,x,y>,%d,0)");
int get_formats(int no, char *p1, char *p2, …)
est une fonction à nombre variable d'arguments où:
no, le 1er argument, est un entier qui indique le numéro d'ordre de la valeur à récupérer (numéro d'ordre du format concerné, dans la chaîne transmise à
new_pattern), ou s'il vaut 0 indique que toutes les valeurs sont à récupérer.
Le ou les arguments suivants sont les adresses des variables C dans lesquelles seront mémorisées la ou les valeurs voulues, si ce ne sont pas des chaînes.
Dans le cas où l'on attend une chaîne de caractères (formats %s ou %I), deux arguments sont nécessaires. Le premier des deux doit être un pointeur sur une zone mémoire dans laquelle Prolog pourra ranger la chaîne, le second doit être un entier qui indique la taille maximum de la chaîne. La fonction retourne 0 si tout c'est bien passé, un numéro d'erreur si une erreur s'est produite.
Voici pour chaque format le type de(s) variable(s) C associée(s): format type C mode de passage
© PrologIA
A!ociation
Prolog
HERITAGE
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 9
%d
%s
%f
%F
%i
%I long adresse char *, int valeur float double adresse adresse long adresse char *, int valeur
Voici un exemple d'appel de get_formats: long i; erreur = get_formats(0, &i);
La fonction get_formats va se charger de garnir l'entier i avec la valeur associée au premier format (l'entier -2 dans notre exemple). A noter que cet appel est strictement équivalent à: erreur = get_formats(1, &i);
8 . 5 . 2 .
Exemple
Voici un exemple d'utilisation des fonctions new_pattern et get_formats: regles_test(recuperation, "recuperation", 1.2e0)->; regles_test(des, "de chaînes", 4444.2e0)->; regles_test(idents, "de", 0.2e0)->; regles_test(Prolog, "caractères", 1.2e0)->; int exemple2()
{
int erreur;
long i;
char s[100];
float f;
erreur = new_pattern("regles_test(%i,%s,%f)");
if (erreur) return error;
while (next_solution() == SOLUTION_EXISTS)
{
erreur = get_formats(0, &i, s, 100, &f);
if (erreur) goto error;
printf("%d %s %f\n",i,s,f);
}
kill_goal();
return 0; error:
printf("erreur\n");
kill_goal();
return erreur;
}
© PrologIA
U 3 - 10
Manuel d'Utilisation Windows
8 . 6 .
Autres fonctions
ConnectDescriptors( EXTERNAL_DESCRIPTOR * paD[] )
Cette routine permet de déclarer dans le code de l'application le tableau de descripteurs paD, écrit comme on l'aurait fait pour une extension C directe. Ce tableau de descripteurs doit être persistant durant toute la session Prolog qui l'utilise (par exemple, qualifié de static s'il est en position de variable locale), et la déclaration doit être faite avant le début de la session (avant l'appel de
ProStart() ).
Si l'argument est NULL, les descripteurs seront supprimés dans la prochaine session. Si la routine est invoquée plusieurs fois, c'est le dernier tableau qui est pris en compte. La routine retourne 0 en cas de succès, -1 en cas d'échec
(session déjà lancée).
ConnectInString( InStringFunction * pfIS )
Cette routine permet de redéfinir la fonction de lecture dans la console, par la déclaration d'une fonction d'entrée de texte pfIS() à laquelle Prolog soumettra toutes les entrées (prédicats). Le remplacement et la suppression de cette fonction fonctionnent comme la précédente. Cette routine retourne la foncion qui était installée avant son appel.
Le format de la fonction de lecture est imposé: son premier argument est l'adresse d'un buffer prêt à recevoir le texte (donc alloué), son second argument est la capacité maximale de ce buffer. Le code de retour de la fonction est ignoré: en cas d'erreur, elle doit rendre une chaîne vide dans le buffer.
Intérieurement, la fonction peut effectuer toute opération même bloquante (en traitant les événements) nécessaire pour obtenir le texte à retourner.
ConnectOutString( OutStringFunction * pfOS )
Cette routine permet de redéfinir la fonction d'écriture dans la console, par la déclaration d'une fonction de sortie de texte pfOS() à laquelle Prolog soumettra toutes les sorties (prédicats, messages). Le remplacement et la suppression de cette fonction fonctionnent comme la précédente. Cette routine retourne la foncion qui était installée avant son appel.
Le format de la fonction d'écriture est imposé: son unique argument est l'adresse d'un buffer contenant le texte à imprimer. Le code de retour de la fonction est ignoré: aucune erreur n'est attendue.
Intérieurement, la fonction peut effectuer toute opération même bloquante (en traitant les événements) nécessaire pour afficher le texte. Toutefois, on remarquera que Prolog peut émettre des lignes vides, et donc un filtrage peut
être nécessaire si par exemple des boîtes de messages sont utilisées.
A!ociation
Prolog
HERITAGE
© PrologIA
A!ociation
Prolog
HERITAGE
Spécificités de Prolog II + Windows
U 3 - 11
get_error_complement( int lgmax, char *str, int in_external_code )
Cette routine permet d'obtenir sous forme de chaîne de caractères le terme correspondant à l'éventuel complément d'erreur remonté par l'exécution d'un but. Elle doit être appelée avant la fonction kill_goal(). La variable str (chaîne de caractères) doit avoir été allouée auparavant et doit être d'une longueur supérieure ou égale à l'entier lgmax passé en premier paramètre. Si le booléen
in_external_code est non nul, cela signifie que l'on désire obtenir la chaîne en code caractère externe.
© PrologIA
A!ociation
Prolog
HERITAGE

Link público atualizado
O link público para o seu chat foi atualizado.