Skill, a simple knowledge inference logic language.

"Do it with Skill"
- Google in 'Results 1 - 10 of about 5,810 for "Do it with Skill". (0.47 seconds)'

Skill-0 (présenté ici) est une version de test d'une maquette d'un langage d'inférence en cours de conception.
Skill offre quelques ressemblances avec Prolog mais s'en distingue par des particularités importantes et des limitations différentes.

Skill n'est pas basé sur la logique des prédicats du premier ordre comme l'est Prolog, mais simplement sur du 'pattern matching'. Pour cette raison il n'est pas limité à cette logique et peut être un outil pour appréhender d'autres logiques d'ordre supérieur (de manière très limitée pour le moment), c'est une maquette 'quick and dirty', ne l'oublions pas...

1 Introduction.

Dans cette introduction, il sera fait quelques références à Prolog, si vous ne connaissez pas Prolog ce n'est pas grave (je ne le connais que très superficiellement) ça ne vous empêchera pas de comprendre la suite.

Un database de Skill est composé de faits et de règles qui sont décrits par une même syntaxe.

1.1 Les faits.

Voici un exemple de fait:

(Romeo loves Juliet)

Vous avez probablement une idée de ce que cela peut vouloir dire, Skill n'en a aucune.
vous auriez pu écrire (loves Romeo Juliet) qu'il n'en aurait pas plus été ému.

En Prolog, vous auriez peut-être écrit:

loves(romeo,juliet).
Pour Skill, une majuscule n'a aucune signification particulière, elle est juste différente de la minuscule correspondante, en gros comme dans beaucoup de langages de programmation, ici Romeo commence par une majuscule parce que c'est mon habitude de faire commencer les noms propres par une majuscule, c'est tout.

Skill n'a aucune notion de ce qu'est un prédicat, on peut considérer que 'loves', 'Romeo' et 'Juliet' ne sont que des noms dans un même ensemble de noms.
La sémantique et la cohérence des sources est un problème de celui qui l'écrit, pas un problème de Skill

1.2 Les règles.

Voici un exemple de règles:

( (?x happy) <-  [ 
                    [ (?x loves ?y) (?y loves ?x) ]
                    [ (?x sleeps) ]
                ] )

Cela peut vouloir dire que quelqu'un (?x) est heureux s'il aime une autre personne (?y) qui l'aime, ou encore s'il dort.

En Prolog, vous auriez pu écrire:

happy(X) |- loves(X, Y), loves(Y, X);
            sleeps(X).

La construction Skill est appelée rule-unit

En Skill, '<-' dénote une inférence possible, la partie droite étant une liste de termes pouvant aboutir à l'obtention du fait situé à gauche.
Pour que Skill puisse inférer par cette rule-unit, il suffit qu'un de ces termes ( par exemple [ (?x sleeps) ] ) 'matche' la database. Par analogie avec des langages de programmation, on peut dire que ces termes sont connectés par un ou.

Les termes sont une liste de conditions à remplir, il faut que toutes ces conditions soient remplies. On peut dire que ces conditions sont connectées par un et.
En Skill, cette liste de conditions, par exemple [ (?x loves ?y) (?y loves ?x) ] est appelée rule.

?x, ?y, ?foo, ?bar ?421,5 etc. sont des variables non-liées, qui peuvent être instanciées au moment de l'inférence en les 'matchant' à la database par un processus équivalent à l'unification en Prolog.
Une même variable dans un rule_unit doit être instanciée par une même entité, sa portée est limitée à ce rule-unit.

On aurait pu aussi utiliser deux rule-units.

( (?x happy) <-  [ [ (?x loves ?y) (?y loves ?x) ] ] )
( (?someone happy) <-  [ [ (?someone sleeps) ] ] )

Pour l'instant, disons que c'est équivalent.

Un ensemble de rule-units pouvant conduire à une inférence conduisant à un même fait est appelé rule-set. C'est un concept qui a surtout une valeur au moment de l'inférence. les rule-units d'un même rule-set n'ont pas nécessairement une partie gauche qui corresponde quand aux variables liées.

Par exemple:

( (?x friend ?y) <-  [ [ (?x loves ?y) ] ] )
( (?x ?y ?z) <-  [ [ (?z ?y ?x) (?y symetric) ] ] )
et ces faits :
(Romeo loves Juliet)
(friend symetric)   # naive and this is a comment
la première rule_unit peut aboutir à inférer : (Romeo friend Juliet)
la seconde rule_unit peut aboutir à inférer : (Juliet friend Romeo) après avoir inféré (Romeo friend Juliet).
Cet exemple est dans la distribution dans la database ex-01, on en reparlera plus loin.

1.3 Interrogation.

Skill a surtout pour ambition d'être utilisé à partir de langages de programmation par l'intermédiaire d'une API.
Cette API n'est pas encore bien définie dans les détails mais elle est utilisée par un interpréteur de commandes écrit à la hâte.
Cet interpréteur charge des databases en mémoire en utilisant le parser et répond aux questions du clavier, nous allons l'utiliser sur une database.
Cet exemple est dans la distribution dans la database verone-01.

Faits :

(Romeo loves Juliet)
(Juliet loves Romeo)
(Romeo family Montague)
(Juliet family Capulet)
(Capulet ennemy Montague)
(Montague ennemy Capulet) 

Règles :

( (?x happy) <- [[ (?x loves ?y) (?y loves ?x) ]] ) 

Interrogeons cette database dans laquelle se dessine déja une tragédie :

pat@harpo:~/soft/skill/test$ ./inter.rb
:: use verone-01
verone-01:: ? (Romeo is veronese)
I don't know.
query complete, 0.001336 0.0 0.0 

La première ligne est l'invocation de l'interpréteur de commande à partir du prompt du shell. L'interpréteur nous prompte alors par ':: '.
Nous lui demandons alors d'utiliser la database 'verone-01' dans laquelle nous avons introduit ces faits et une règle. L'interpréteur nous prompte alors de manière différente 'verone-01:: ' de manière à nous indiquer quelle database nous utilisons.
Afin de vérifier que Skill n'a pas la connaissance innée de Shakespeare, nous l'interrogeons :
? (Romeo is veronese)
Ici le '?' est le nom de la commande, il doit être séparé des arguments par un ou des espaces. L'argument est un fait dont nous nous enquérons.
Skill répond prudemment qu'il ne sait pas... Il ne fait pas l'hypothèse d'un monde clôt, les faits qu'il ne peut trouver ou inférer n'en deviennent pas faux. C'est une différence majeure avec Prolog. Elle permettra à Skill d'être utilisé pour des logiques non-classiques.
Suit l'indication qu'il en a terminé avec cette requête stupide, il affiche également le temps écoulé, le temps CPU user et system.

Continuons:

verone-01:: ? (Romeo loves Juliet)
('Romeo' 'loves' 'Juliet' : true)
verone-01::> ?
 line 1
verone-01::> q
1 items.
query complete, 130.599171 0.0 0.0 

Nous vérifions que Romeo aime toujours Juliet, Skill nous répond qu'il a bien trouvé le fait : ('Romeo' 'loves' 'Juliet' : true)
Il nous prompte alors d'une manière différente et nous trouvons engagé avec lui dans un sous-dialogue à propos de ce fait.
La question principale que nous pouvons lui poser est '?', que nous aurions pu appeler 'pourquoi'.
Skill nous réponds que c'est un fait défini dans la ligne 1 (des faits).
Pour sortir de ce sous-dialogue peu passionnant nous pouvons utiliser la commande 'q' (quitter).

verone-01:: ? (Juliet happy)
('Juliet' 'happy' : true)
verone-01::> ?
 since :
  ('Juliet' 'loves' 'Romeo' : true) line 2
  ('Romeo' 'loves' 'Juliet' : true) line 1
verone-01::> q
1 items.
query complete, 14.683846 0.0 0.0 

Rien de bien différent dans le dialogue mais la réponse de Skill nous montre qu'il a inféré le fait recherché.

verone-01:: ? (Romeo happy)
('Romeo' 'happy' : true)
verone-01::> ?
 since :
  ('Romeo' 'loves' 'Juliet' : true) line 1
  ('Juliet' 'loves' 'Romeo' : true) line 2
verone-01::> q
1 items.
query complete, 5.924045 0.0 0.0 

C'est merveilleux, Romeo et Juliet s'aiment et sont heureux !

verone-01:: ? (Juliet ?x ?y)
('Juliet' 'loves' 'Romeo' : true)
verone-01::> n
('Juliet' 'family' 'Capulet' : true)
verone-01::> n
2 items.
query complete, 5.156055 0.0 0.0
verone-01:: ? (Juliet ?x)
('Juliet' 'happy' : true)
verone-01::> n
1 items.
query complete, 2.019048 0.0 0.0
verone-01:: ? (?x ?y Juliet)
('Romeo' 'loves' 'Juliet' : true)
verone-01::> n
1 items.
query complete, 2.124093 0.01 0.0

Nous avons voulu en savoir plus au sujet de Juliet en utilisant des variables dans l'interrogation.
Nous voyons que Skill ne fait aucune différence entre ce qui pour nous peut sembler un nom de prédicat ou de relation comme 'loves' et des noms d'objets comme 'Romeo'.
Pour passer d'une réponse à une autre, nous avous utilisé la commande 'n' (next) mais un simple 'enter' produit le même résultat.

verone-01:: ? (Romeo ennemy Juliet)
I don't know.
query complete, 0.001353 0.0 0.0
verone-01:: q
pat@harpo:~/soft/skill/test$ 

Ca devenait lassant, nous sommes revenu au prompt du shell par la commande 'q' au premier niveau de dialogue.

Par la suite, dans les exemples, nous améliorerons cette database qui n'est pas de nature à attirer des foules dans les théatres.
Vous pouvez passer tout de suite à ces exemples qui vous montreront les spécificités de Skill à moins que vous ne préfèriez d'abord avoir plus de précisions sur le langage.

2 Le langage.

Si ce chapitre vous ennuie, lisez le rapidement, les exemples devraient suffire pour vous faire une idée.

Le langage devrait subir des changements mais la syntaxe générale ne sera sans doute pas modifiée profondemment.
Il y aura surtout une amélioration des possibilités de définition des faits et des règles.

2.1 La syntaxe.

La description formelle de la syntaxe se trouvera sans doute un jour dans un appendice de ce document.

Le parser a la qualité d'avoir été rapidement écrit, il a une bonne partie des défauts que peut avoir un parser, notamment de ne pas correspondre parfaitement à ce qui est écrit plus bas.

2.1.1 Le Triplet.

Un source de Skill est une suite de triplets, les faits et rule-units sont des triplets.
(Romeo loves Juliet) est un triplet.
Un rule-unit aussi, (Romeo sleeps) aussi.

Un triplet est une objet composé de 3 plets au maximum et d'une valeur. Ces plets sont ordonnés, surtout quand il y en a plusieurs.
Il se note :
(plet1 plet2 plet3 : valeur-du-triplet)

plet3, plet2 et plet3, plet1 plet2 et plet3 sont optionnels, il leur est par défaut affecté _none_, cette valeur a une valeur sémantique pour Skill, ainsi que la valeur _any_.

La valeur du triplet est optionnelle, la valeur par défaut est true. true ne correspond à aucune notion sémantique pour Skill, simplement, par pure bienveillance, le parser l'affecte par défaut sachant que l'ont dit le plus souvent des choses que l'on pense vraies.

Les plets et la valeur du triplet peuvent être :

( ) est le plus petit triplet possible, il ne veut rien dire mais il a la valeur true.

(421 ([a [b (c : _)] d] foo) : [maybe, -5]) est aussi un triplet, je ne sais pas ce qu'il veut dire, Skill non plus.

Les 2 triplets suivants sont équivalents:
( Romeo loves Juliet)
('Romeo' 'loves' "Juliet" : true)

2.1.2 Autres.

Nous ne nous attarderons pas plus sur les listes et les nombres.

On peut également placer des commentaires qui commencent par le caractère # et se termine par la fin de la ligne, pour l'instant ils ne peuvent être placés qu'entre des triplets.

Il est également prévu des directives permettant d'inclure des fichiers sources.

2.2 Sémantique.

Bien que ces structures de données soient acceptées par le parser, à un niveau sémantique, toutes ne sont pas acceptées suivant le contexte. En fait dans cette maquette il y a peu de tests de ce qu'envoie le parser, et des erreurs conduisent généralement à une exception dans le programme ce qui vous donnera peu de renseignements sur l'erreur dans vos sources.

Plutot que de détailler ce qui est permis dans les faits et les règles, disons que cela correspond aux exemples.
dans l'avenir, il y aura plus de possibilités, notamment de mettre Des triplets et des listes dans des triplets de faits.

2.3 Organisation interne.

Ce chapitre ne devrait pas se trouver ici, mais je ne sais pas où le mettre.

Les triplets sont stockés dans une database en mémoire ayant une structure complètement différente des sources.
Il est assez compliqué d'expliquer cette structure sans faire de croquis, mais le concept est assez simple lorsqu'il est compris. Disons juste que la structure est basée sur une utilisation intensive de hashes qui assurent un temps d'accès constant (en négligeant le swap) quelque soit la taille de la database.
Ce temps n'étant pas court, il défavoriserait plutôt les databases 'jouet' comme celles des exemples présentés ici pour lesquels une implémentation naïve aurait été bien plus efficace.

Enfin, on devrait plutôt dire un temps théoriquement constant, ce qui ne veut pas dire grand chose de bon pour un utilisateur sinon que le problème ne vient pas de Skill, ce défaut peut être corrigé et Skill devrait bien 'scaler' sur des databases de plusieurs centaines de milliers de faits et de règles et plus.

2.4 La database.

Nous parlons ici de la database source. Il s'agit d'un répertoire.

Elle contient principalement 2 fichiers, Facts.skl et Rules.skl.
Il est complètement stupide (bien que cela m'ait fait gagner un peu de temps) de séparer artificiellement ainsi les faits et les règles, ce point sera revu, mais un certain nombre de fichiers sources dans un répertoire pourrait convenir.

3 Exemples.

3.1 Exemple simple.

Profitons d'un entracte de la pièce pour revenir à l'exemple ex-01 dont il a été question dans le chapitre sur les règles et dont vous pouvez trouver la database dans la distribution.
Rappelons le contenu de la database :

Database ex-01
Les règles :
( (?x friend ?y) <-  [ [ (?x loves ?y) ] ] )
( (?x ?y ?z) <-  [ [ (?z ?y ?x) (?y symetric) ] ] )
Les faits :
(Romeo loves Juliet)
(friend symetric) # naive and this is a comment

Nous pouvons interroger cette database :

pat@harpo:~/soft/skill/test$ ./inter.rb
:: use ex-01
ex-01:: ? (Juliet friend Romeo)
('Juliet' 'friend' 'Romeo' : true)
ex-01::> ?
 since :
  ('Romeo' 'friend' 'Juliet' : true) since :
    ('Romeo' 'loves' 'Juliet' : true) line 1
  ('friend' 'symetric' : true) line 2
ex-01::>
1 items.
query complete, 13.169176 0.01 0.0 

Nous aurions tendance à considérer 'loves', 'friend' et 'symetric' comme des noms de relations ou de prédicats, ces notions n'ont aucune pertinence pour Skill, il ne s'y connait pas plus que moi en logique, il s'essaie juste au 'pattern matching'.

Cet exemple nous montre que des variables libres comme ?y du second rule-unit peut être matchées par ce que l'on considère comme une relation (friend). Il nous montre aussi que ce qui peut être compris comme une relation comme 'friend' peut aussi être compris comme un objet dans le fait (friend symetric).
Il s'agit encore d'une différence majeure avec Prolog et elle permettra peut-être un jour à Skill de se confronter à des logiques d'ordre supérieur.

3.2 To be or not to be.

Mais que vient faire Hamlet à Verone ?

Nous avons un peu étoffé la database de Verone et l'avons appelée verone-02

Faits :
(Romeo loves Juliet)
(Juliet loves Romeo)
(Romeo family Montague)
(Juliet family Capulet)
(Capulet ennemy Montague)
(Montague ennemy Capulet)
(Mercutio friend Romeo)
Règles :
# Reasons to be happy
( (?x happy) <- [ [ (?x loves ?y) (?y loves ?x) ] ] )

# Reasons not to be happy
( (?x happy : false) <- [ [ (?x loves ?y) (?y loves ?x : false) ] ] )
( (?x happy : false) <- 
  [
    [ (?x loves ?y) (?x ennemy ?y) ]
    [ (?x loves ?y) (?y ennemy ?x) ]
  ]
)

# Who is an ennemy ?
( (?x ennemy ?y) <- 
  [
    [ (?x family ?a) (?y family ?b) (?a ennemy ?b) ]
    [ (?x friend ?a) (?a ennemy ?y) ]
  ]
)

Interrogeons cette database.

ex-01:: use verone-02
verone-02:: ? (Romeo happy : _)
('Romeo' 'happy' : false)
verone-02::> ?
 since :
  ('Romeo' 'loves' 'Juliet' : true) line 1
  ('Romeo' 'ennemy' 'Juliet' : true) since :
    ('Romeo' 'family' 'Montague' : true) line 3
    ('Juliet' 'family' 'Capulet' : true) line 4
    ('Montague' 'ennemy' 'Capulet' : true) line 6
verone-02::>
('Romeo' 'happy' : true)
verone-02::> ?
 since :
  ('Romeo' 'loves' 'Juliet' : true) line 1
  ('Juliet' 'loves' 'Romeo' : true) line 2
verone-02::>
2 items.
query complete, 15.358368 0.02 0.0 

Nous pouvons d'abord remarquer que la première commande nous fait passer de la database ex-01 à la database verone-02, elle la remplace comme database courante, mais la database courante précédente est conservée et ne sera pas chargée à partir des fichiers la prochaine fois qu'on voudra l'utiliser.
Dans un avenir incertain, des requêtes pourrons s'échanger entre des databases... Mais revenons à Verone...

La requête suivante ? (Romeo happy : _) utilise l'underscore '_' comme valeur du triplet, c'est la valeur 'catch-all' pour obtenir toutes les valeurs du triplet (Romeo happy). Notons aussi que la valeur catch-all ne marche pas bien à la place d'un plet, utilisez une variable...

Accessoirement le résultat peut nous plonger dans la perplexité. Il peut y avoir plusieurs valeurs pour ce triplet et elles peuvent nous sembler inconsistentes.
Cela parce que nous associons true à vrai, et false à faux et que ce résultat déplairait à Aristote.

Skill est ni aristotélicien ni non-aristotélicien et il ne le sait pas.
Nous pouvons l'interroger :

:: ? (Skill aristotelicien)
No current database.

Cette particularité n'a rien d'un bug, elle m'a demandé du temps pour la concevoir et l'implémenter ce qui pour un programmeur est du sang, de la sueur et des larmes.

Pour Skill, le 'ou' de 'être ou ne pas être' n'est pas exclusif, cela ne le fait pas sourciller qu'on puisse à la fois être et ne pas être.

Sans doute dans votre ainsi-nommé 'monde réel' (1) les choses ne peuvent pas être à la fois vraies et fausses et vous vous attendiez peut-être à ce que que Skill détecte cette horreur que peut être une inonsistence dans une database.

Il ne le fait pas naturellement car il n'est pas piloté par la logique, mais comme vous connaissez la logique classique vous pouvez essayer de la lui apprendre.

Par exemple, en rajoutant ces triplets dans les règles :
( (inconsistent) <- [[ (?x ?y ?z) (?x ?y ?z : false) ]] )
( (inconsistent) <- [[ (?x ?y) (?x ?y : false) ]] )
( (inconsistent) <- [[ (?x) (?x : false) ]] )
Appelons cette database verone-02.1

Essayons:

verone-02:: use verone-02.1
verone-02.1:: ? (inconsistent)
('inconsistent' : true)
verone-02.1::> ?
 since :
  ('Juliet' 'happy' : true) since :
    ('Juliet' 'loves' 'Romeo' : true) line 2
    ('Romeo' 'loves' 'Juliet' : true) line 1
  ('Juliet' 'happy' : false) since :
    ('Juliet' 'loves' 'Romeo' : true) line 2
    ('Juliet' 'ennemy' 'Romeo' : true) since :
      ('Juliet' 'family' 'Capulet' : true) line 4
      ('Romeo' 'family' 'Montague' : true) line 3
      ('Capulet' 'ennemy' 'Montague' : true) line 5
verone-02.1::>
+ Warning : 1 recursion checks failed.
+ Error : Some results may be missing or erroneous.
1 items.
query complete, 7.549202 0.02 0.0

Si vous cherchez la raison du Warning, vous pouvez utiliser la commande 't' (trace) qui permet d'afficher le déroulement de l'exécution par des messages plutôt incompréhensibles mais parfaitement indentés.
Pour supprimer cette trace, utilisez la commande 'ut' (untrace).

Ici, c'est à cause de la dernière règle ajoutée :
( (inconsistent) <- [[ (?x) (?x : false) ]] )
Skill va instancier ?x par inconsistent et cela entraine une récursion.
pour se débarasser de l'erreur, on peut supprimer la règle, c'est pour le moment la seule solution.

Peut-être pensez-vous avoir enseigné à Skill les notions de vrai et de faux, peut-être même pensez-vous lui apprendre la notion de bien et de mal ?
Continuons...

verone-02.1:: ? (inconsistent : false)
I don't know.
query complete, 0.000881 0.0 0.0
Indécrottable !

On peut cependant inférer à partir de faits non trouvés. C'est l'objet de l'exemple suivant.

3.3 Let's go.

Construisons cette database et appellons là none:

Règles :
( (go "to the beach") <- [[ (clouds : _none_) ]] )
et créons un fichier de faits vide.
:: use none
none:: ? (go ?x)
('go' 'to the beach' : true)
none::> ?
 since :
  ('clouds' : _none_) Nothing found.
none::>
1 items.
query complete, 2.754502 0.01 0.0

On peut lui demander s'il y a du soleil.

none:: ? (sun)
I don't know.
query complete, 0.000781 0.0 0.0
none:: ? (sun : _none_)
('sun' : _none_)
none::> ?
 Nothing found.
none::>
1 items.
query complete, 27.875197 0.01 0.0

3.4 Règles récursives.

Des exemples analogues sont souvent utilisés pour expliquer la récursion en Prolog.

Database ancestor.
Faits :
(jo1 parent jo2)
(jo2 parent jo3)
(jo3 parent jo4)
(jo4 parent jo5)
(jo5 parent jo6) 
Règles :
( (?x ancestor ?y) <- [ 
                            [ (?x parent ?z) (?z ancestor ?y) ]
                            [ (?x parent ?y) ]
                      ] )

Ce 'rule-unit' est formé de 2 règles. A la différence des contraintes imposées par Prolog, l'ordre de ces 2 règles est indifférent, Skill les ordonne lui-même (peut-être pas toujours très bien).
Par contre, pour le moment et comme en Prolog, dans la règle [ (?x parent ?z) (?z ancestor ?y) ], les conditions doivent être ordonnées pour pouvoir mettre un terme à la récursion.
Il semble que ce soit le seul résidu procédural dans Skill. Il est neanmoins important.

Essayons :

ancestor:: ? (?x ancestor jo4)
('jo3' 'ancestor' 'jo4' : true)
ancestor::>
('jo1' 'ancestor' 'jo4' : true)
ancestor::> ?
 since :
  ('jo1' 'parent' 'jo2' : true) line 1
  ('jo2' 'ancestor' 'jo4' : true) since :
    ('jo2' 'parent' 'jo3' : true) line 2
    ('jo3' 'ancestor' 'jo4' : true) since :
      ('jo3' 'parent' 'jo4' : true) line 3
ancestor::>
('jo2' 'ancestor' 'jo4' : true)
ancestor::>
3 items.
query complete, 19.525322 0.03 0.01

On peut vouloir savoir qui est qui :

ancestor:: ? (?a ?b ?c)
('jo4' 'parent' 'jo5' : true)
ancestor::>
('jo5' 'parent' 'jo6' : true)
ancestor::>
('jo1' 'parent' 'jo2' : true)
ancestor::>
('jo2' 'parent' 'jo3' : true)
ancestor::>
('jo3' 'parent' 'jo4' : true)
ancestor::>
('jo3' 'ancestor' 'jo4' : true)
ancestor::>
('jo4' 'ancestor' 'jo5' : true)
ancestor::>
('jo5' 'ancestor' 'jo6' : true)
ancestor::>
('jo1' 'ancestor' 'jo2' : true)
ancestor::>
('jo2' 'ancestor' 'jo3' : true)
ancestor::>
('jo3' 'ancestor' 'jo5' : true)
ancestor::>
('jo3' 'ancestor' 'jo6' : true)
ancestor::>
('jo4' 'ancestor' 'jo6' : true)
ancestor::>
('jo1' 'ancestor' 'jo3' : true)
ancestor::>
('jo1' 'ancestor' 'jo4' : true)
ancestor::>
('jo1' 'ancestor' 'jo5' : true)
ancestor::>
('jo1' 'ancestor' 'jo6' : true)
ancestor::>
('jo2' 'ancestor' 'jo4' : true)
ancestor::>
('jo2' 'ancestor' 'jo5' : true)
ancestor::>
('jo2' 'ancestor' 'jo6' : true)
ancestor::>
20 items.
query complete, 14.205102 0.08 0.0

Le temps de 0.08 secondes devient alarmant... Mais bon, c'est une maquette.

Et c'était plutôt 'qui est quoi ?' parce que 'qui est qui ?' :

ancestor:: ? (?a ?R ?a)
I don't know.
query complete, 0.112107 0.1 0.0

... après avoir beaucoup cherché.

3.5 Built-in.

Skill implémente (assez mal) 2 built-in, == (égalité) et != (non-égalité).
Voyons-le sur un exemple.

Database builtin.
Faits :
(class fritz cat)
(class garrigou dog)
(class pif dog)
Règles :
((same-class ?x ?y ) <- [[(class ?x ?z) (class ?y ?z) (?x != ?y)]])
:: use builtin/
builtin/:: ? (same-class ?x ?y)
('same-class' 'garrigou' 'pif' : true)
builtin/::>
('same-class' 'pif' 'garrigou' : true)
builtin/::>
2 items.
query complete, 2.609603 0.01 0.012

Il est plus difficile de trouver des exemples d'utilité de l'égalité.

L'implémentation est mauvaise pour le moment du fait que l'on ne peut comparer utilement que des variables instanciées.
Dans l'exemple, la condition (?x != ?y) doit être placée comme dernière condition. On ne peut se servir de ces builtins pour éviter d'évaluer des conditions.

3.6 Autres exemples.

Je ne voudrais pas dévoiler le dénouement de 'Romeo et Juliette' à ceux qui n'ont pas encore vu la pièce mais vous trouverez des exemples dans la distribution, vous pouvez aussi vous en faire.
Si vous en faites d'intéressants, ils m'intéressent aussi.

Ils vous montreront non seulement des bugs mais aussi les limites actuelles de Skill, tant du point de vue des possibilités que des performances.

4 Divers.

4.1 Commandes de l'interpréteur.

Il y a 2 niveaux d'interprétation des commandes.
le premier niveau qui est celui dans lequel on entre lorsque l'interpréteur nous prompte une première fois, il est caractérisé par le prompt :: éventuellement précédé du nom d'une database lorsqu'on utilise une database appelée database courante.
nom::
On entre dans le second niveau lorsque l'interpréteur nous présente un fait. il est caractérisé par le même prompt suivi du caractère '>' :
nom::>

Certaines commandes peuvent être utilisées aux 2 niveaux, d'autres sont spécifiques à 1 niveau, les autres peuvent être utilisées au 2 niveaux.

Nous ne détaillerons pas les messages d'erreur qui sont explicites.

4.1.1 Commandes communes aux deux niveaux.

<CTRL-C> :
Interrompt l'exécution de l'interpréteur, i.e l'interruption n'est simplement pas trappée.

q (quit):
Revient au niveau inférieur, au prompt du shell quand on est au premier niveau.

t (trace) et ut (untrace) :
Trace de l'exécution des requêtes. Du temps des télétypes, elle détruisait des forêts.

4.1.2 Commandes du premier niveau.

<enter> :
Ne fait rien sinon re-afficher le prompt.

use nom :
la database de nom 'nom' devient la database courante, elle est chargée si elle ne n'est pas déjà en mémoire.

del nom :
la database de nom 'nom' est supprimée de la mémoire si elle y est.

refresh nom :
comme del suivi de use.

? liste de triplets :
recherche les faits matchants chaque triplet.
Si la liste est vide, il essaie de chercher les faits ayant la valeur true. Dans ce cas, il est équivalent à :
? (?x ?y ?z) (?x ?y) (?x)

lf et lsf liste de triplets :
Comme '?' mais n'infère pas, il ne propose pas non plus de second niveau de dialogue. Il affiche seulement les faits initiaux, son intérêt n'est pas seulement préhistorique, il peut servir à mesurer les temps d'accès en fonction de la taille des databases.
lsf trie la liste.

m nombre :
Met à 'nombre' le niveau maximal d'imbrication des règles à l'exécution qui est arbitrairement à 16 par défaut.

4.1.3 Commandes du second niveau.

Aucune commande ne prend d'argument à ce niveau.

n ou <enter> :
passe au fait suivant ou sort du second niveau s'il n'y a plus de faits à afficher.

? :
Donne la source du fait.

i :
Donne les faits utilisés, inférés ou non, y compris des faits intermédiaires non utilisés en fin de compte.
Laisse parfois entendre le peu d'intelligence de Skill dans ses recherches...

5 Bugs et déficiences.

Je ne suis pas très intéressé par les rapports de bugs dans Skill, j'ai déjà tout ce qu'il faut comme bugs à la maison et cette maquette n'est pas destinée à être maintenue.
Mais si vous êtes ennuyés par des bugs, n'hésitez pas à m'en parler.

Je suis beaucoup plus intéressé par des suggestions et des critiques avant de passer à une autre maquette, car celle-ci est mal écrite, buggée et ne permet pas grand chose. On ne peut même pas implémenter 'Hello World!' !
Elle a aussi beaucoup souffert des errements dans le design pour lequel elle a surtout été un support de réflexion. Continuer avec cette maquette serait maintenant une perte de temps pour ce design, j'ai déjà trop joué avec.

Le but de Skill-0 était de valider une structure unificatrice de données (en prenant 'unification' dans le sens utilisé dans Prolog).
Il y a partiellement réussi mais il ne peut rendre compte que très imparfaitement des performances que peut apporter une telle structure, celles-ci n'ont été validées que pour des accès simples.
Cela vient du fait qu'il n'existe pas d'itérateur externe en Ruby et qu'il n'est pas possible d'en construire sur des hashes, sinon en utilisant les continuations dont le coût en CPU et en mémoire est prohibitif ou, solution choisie, en transformant préalablement des hashes en arrays ce qui ferait perdre une bonne partie de l'intérêt de l'organisation des données qui est destinée à éviter de scanner des listes.
De plus l'implémentation des hashes dans Ruby ne garantit pas un temps constant d'accès, la solution est simple, mais une modification de Ruby ne suffirait pas à résoudre le problème de la non-possibilité raisonable d'itérateurs externes sur des hashes et je n'ai pas envie d'entretenir un clone de Ruby...

Je tiens à préciser que je considère Ruby comme un excellent langage pour (entre autres choses) faire des maquettes ou des applications complexes. Il ne convenait tout simplement pas dans ce cas précis pour des besoins qui se présentent rarement.
Il m'a d'ailleurs permis de faire une maquette au code moins volumineux que cette page html pourtant sobre.

Une autre possibilité aurait été d'utiliser Python qui propose des itérateurs externes mais la conjecture n'est pas suffisamment favorable pour que j'embauche un Pythoniste.

La prochaine maquette sera probablement écrite en C (que ne faut-il pas imaginer pour trouver un pretexte pour écrire en C...), mais avant tout, le design doit être repris en tenant compte de cette expérience.

6 Installation.

Personnellement, je me refuse à installer des programmes aussi buggés, je n'ai donc pas prévu de procédure d'installation.

Skill-0 est cependant disponible pour le plaisir des petits et des grands sous la licence Ruby qui vous permet, en gros, d'en faire ce que vous voulez sinon empêcher quiconque de faire de même.

Skill-0 est protégé par les droits d'auteur :
Copyright (C) 2007 Patrick Davalan. All rights reserved.
(Il est encore mieux protégé par une mauvaise programmation).

Téléchargez Skill-0 en choisissant la version la plus récente, une fois décompacté il produit un répertoire skill-0.x (x étant la sous-version) dans le répertoire courant.

Le programme inter.rb est prévu pour s'exécuter dans cette directory, Tapez juste :
./inter.rb
au moins sous un Unix, sous windows peut-être :
ruby inter.rb
qui devrait marcher partout.

Utiliser Skill-0 necessite d'avoir Ruby et ses bibliothèques standard installés. Dans certaines distributions comme Debian, Les bibliothèques standard de Ruby sont réparties en plusieurs packages. Skill-0 ne demande rien de particulier sinon la bibliothèque Ruby standard qui interface avec GNU Readline, elle est dans un package Debian séparé, installez-le.

Testé avec Ruby 1.8.4, marche peut-être aussi mal avec Ruby 1.6, I don't know.

Les databases exemples sont installées comme sous-répertoires du répertoire courant, pour en faire d'autres prenez-en une comme exemple.

En cas de problème, contactez-moi : almazz à wanadoo point fr.

7 Remerciements.

Je tiens à remercier tout particulièrement William Shakespeare pour les exemples qu'il m'a fournis, notamment par sa pièce Romeo et Juliette sans laquelle ce travail n'aurait pas été possible.
Son aide désintéressée m'a été précieuse. Ses pièces sont libres car dans le domaine public. Enjoy his tragedies !

Notes.

(1) Bien que le 'real world' soit un canular qui date des débuts d'usenet, beaucoup de gens, encore aujourd'hui, continuent à croire en son existence.

keywords : skill, simple knowledge inference logic language
1 Introduction.
1.1 Les faits.
1.2 Les règles.
1.3 Interrogation.
2 Le langage.
2.1 La syntaxe.
2.2 Sémantique.
2.3 Organisation interne.
2.4 La database.
3 Exemples.
3.1 Exemple simple.
3.2 To be or not to be.
3.3 Let's go.
3.4 Règles récursives.
3.5 Built-in.
3.6 Autres exemples.
4 Divers.
4.1 Commandes
5 Bugs et déficiences.
6 Installation.
7 Remerciements.
Notes.
OSI Certified Open Source Software