Nous concevons un langage de programmation multi-paradigme. Partie 5 - Caractéristiques de la programmation logique

Nous continuons l'histoire de la création d'un langage de programmation multi-paradigme qui combine un style logique déclaratif avec un style orienté objet et fonctionnel, ce qui serait pratique lorsque vous travaillez avec des données semi-structurées et intégrez des données provenant de sources disparates. Le langage sera constitué de deux composants étroitement intégrés l'un à l'autre: le composant déclaratif sera chargé de décrire le modÚle de domaine, et le composant impératif ou fonctionnel sera chargé de décrire les algorithmes pour travailler avec le modÚle et le calcul.



Dans le dernier articleJ'ai commencé mon histoire sur le composant de modélisation de langage hybride. C'est un ensemble de concepts-objets reliés par des relations logiques. J'ai réussi à parler des principaux moyens de définir les concepts, l'héritage et de définir la relation entre eux. Les raisons qui m'ont poussé à commencer à concevoir un langage hybride, et ses fonctionnalités, se trouvent dans mes publications précédentes sur ce sujet. Des liens vers eux se trouvent à la fin de cet article.



Et maintenant je propose de plonger dans certaines des nuances de la programmation logique. Puisque le langage du composant de modélisation a une forme logique déclarative, il sera nécessaire de résoudre des problÚmes tels que la définition de la sémantique de l'opérateur de négation, l'introduction d'éléments de logique d'ordre supérieur et l'ajout de la possibilité de travailler avec des variables logiques. Et pour ce faire, vous devrez faire face à des problÚmes théoriques tels que l'hypothÚse de l'ouverture / fermeture du monde, le déni comme refus, une sémantique de modÚle stable et une sémantique bien fondée. Et aussi avec la façon dont les possibilités de la logique d'ordre supérieur sont implémentées dans d'autres langages de programmation logiques.



Commençons par les variables booléennes



Dans la plupart des langages de programmation logique, les variables sont utilisées comme désignation symbolique (espace réservé) d'instructions arbitraires. Ils se produisent aux positions des arguments des prédicats et relient les prédicats les uns aux autres. Par exemple, dans la rÚgle suivante du langage Prolog, les variables jouent le rÎle d'objets X et Y , reliés par des relations: frÚre , parent , mùle et inégalité:



brothers(X,Y) :- parent(Z,X), parent(Z,Y), male(X), male(Y), X\=Y.
      
      





Dans le composant de modélisation, le rÎle des arguments de terme est principalement joué par les attributs de concept:



concept brothers (person1, person2) FROM
parent p1 (child = person1),
parent p2 (child = person2),
male(person: person1),
male(person: person2)
WHERE p1.parent = p2.parent AND person1 != person2
      
      





Ils sont accessibles directement par nom, comme en SQL. Bien que la syntaxe proposée semble plus lourde par rapport à Prolog, dans le cas d'un grand nombre d'attributs, cette option sera plus pratique, car elle met l'accent sur la structure de l'objet.



Mais dans certains cas, il serait toujours pratique de dĂ©clarer une variable boolĂ©enne qui ne serait incluse dans les attributs d'aucun des concepts, mais qui serait en mĂȘme temps utilisĂ©e dans l'expression des relations. Par exemple, si une sous-expression a une forme complexe, vous pouvez la dĂ©composer en ses composants en les liant Ă  des variables boolĂ©ennes. De plus, si une sous-expression est utilisĂ©e plusieurs fois, vous ne pouvez la dĂ©clarer qu'une seule fois en l'associant Ă  une variable. Et Ă  l'avenir, utilisez une variable au lieu d'une expression. Afin de distinguer les variables boolĂ©ennes des attributs de concept et des variables des composants de calcul, dĂ©cidons que les noms de variables boolĂ©ennes doivent commencer par le symbole $ .

A titre d'exemple, nous pouvons analyser le concept qui spĂ©cifie l'appartenance d'un point Ă  un anneau, qui est dĂ©crit par les rayons extĂ©rieur et intĂ©rieur. La distance d'un point au centre d'un anneau peut ĂȘtre calculĂ©e une fois, liĂ©e Ă  une variable et comparĂ©e aux rayons:



relation pointInRing between point p, ring r 
where $dist <= r.rOuter 
    and $dist >= r.rInner 
    and $dist = Math.sqrt((p.x – r.x) * (p.x – r.x) + (p.y – r.y) * (p.y – r.y))
      
      





En mĂȘme temps, cette distance elle-mĂȘme a un rĂŽle auxiliaire et ne fera pas partie du concept.



NĂ©gation.



Examinons maintenant un problÚme plus complexe - l'implémentation de l'opérateur de négation dans le composant de modélisation. Les systÚmes de programmation logique incluent généralement, en plus de l'opérateur de négation booléenne, la rÚgle de négation en tant que refus. Il vous permet d'imprimer not p si la dérivation de p échoue . Dans différents systÚmes de représentation des connaissances, le sens et l'algorithme de la rÚgle pour l'inférence de la négation peuvent différer.



PremiĂšrement, il est nĂ©cessaire de rĂ©pondre Ă  la question sur la nature du systĂšme de connaissances en termes d'exhaustivitĂ©. Dans les systĂšmes qui adhĂšrent Ă  "l'hypothĂšse du monde ouvert" , la base de connaissances est considĂ©rĂ©e comme incomplĂšte, de sorte que les dĂ©clarations qui en manquent sont considĂ©rĂ©es comme inconnues. Pas une affirmation ne peut ĂȘtre gĂ©nĂ©rĂ© que si la base de connaissances stocke explicitement l'instruction que pfaux. Un tel dĂ©ni est appelĂ© fort. Les dĂ©clarations manquantes sont considĂ©rĂ©es comme inconnues et non fausses. Un exemple de systĂšme de connaissances utilisant une telle hypothĂšse est le WEB sĂ©mantique. Il s'agit d'un rĂ©seau sĂ©mantique mondial accessible au public formĂ© sur la base du World Wide Web. Les informations qu'il contient sont, par dĂ©finition, incomplĂštes - elles ne sont pas entiĂšrement numĂ©risĂ©es et traduites sous une forme lisible par machine, elles sont rĂ©parties entre diffĂ©rents nƓuds et sont constamment complĂ©tĂ©es. Par exemple, si sur WikipĂ©dia, dans un article sur Tim Berners-Lee, le crĂ©ateur du World Wide Web et l'auteur du concept de Web sĂ©mantique, rien n'est dit sur ses prĂ©fĂ©rences culinaires, cela ne veut pas dire qu'il ne les a pas, l'article est tout simplement incomplet.



L'hypothÚse opposée est «l'hypothÚse que le monde est fermé».... On pense que dans de tels systÚmes, la base de connaissances est complÚte et les déclarations manquantes sont considérées comme inexistantes ou fausses. La plupart des bases de données suivent cette hypothÚse. Si la base de données ne contient pas d'enregistrement sur une transaction ou sur un utilisateur, nous sommes sûrs qu'une telle transaction n'existait pas et que l'utilisateur n'est pas enregistré dans le systÚme (au moins toute la logique du systÚme est basée sur le fait qu'ils n'existent pas).



Les bases de connaissances complÚtes ont leurs avantages. PremiÚrement, il n'est pas nécessaire de coder des informations inconnues - une logique à deux valeurs (vrai, faux) est suffisante au lieu de trois valeurs (vrai, faux, inconnu). DeuxiÚmement, vous pouvez combiner l'opérateur de négation booléen et la vérification de la dérivabilité d'une instruction de la base de connaissances en un opérateur de négation en tant que refus(négation comme échec). Il renverra true non seulement si l'instruction selon laquelle l'instruction est fausse est stockée, mais également s'il n'y a aucune information à ce sujet dans la base de connaissances. Par exemple, la

rĂšgle



p <— not q





déduit que q est faux (car il n'y a aucune déclaration indiquant que c'est vrai) et p est vrai.



Malheureusement, la sémantique du déni en tant que rejet n'est pas aussi évidente et compliquée qu'il n'y paraßt. Dans différents systÚmes de programmation logique, sa signification a ses propres caractéristiques. Par exemple, dans le cas des définitions cycliques:



p ← not q
q ← not p
      
      





la résolution SLDNF classique (SLD + Negation as Failure) utilisée dans le langage Prolog échouera. La sortie de l'instruction p nécessite la sortie de q , et q - dans p , la procédure de sortie tombera dans une boucle infinie. Dans Prolog, ces définitions sont considérées comme non valides et la base de connaissances est considérée comme incohérente.



En mĂȘme temps, ces dĂ©clarations ne sont pas un problĂšme pour nous. Intuitivement, nous comprenons ces deux rĂšgles comme disant que p et qont des significations opposĂ©es, si l'une d'elles est vraie, alors l'autre est fausse. Par consĂ©quent, il est souhaitable que l'opĂ©rateur de la nĂ©gation en tant que refus puisse travailler avec de telles rĂšgles, de sorte que les constructions de programmes logiques soient plus naturelles et comprĂ©hensibles pour une personne.



En outre, la cohĂ©rence de la base de connaissances n'est pas toujours rĂ©alisable. Par exemple, parfois, les dĂ©finitions de rĂšgles sont dĂ©libĂ©rĂ©ment sĂ©parĂ©es des faits afin que le mĂȘme ensemble de rĂšgles puisse ĂȘtre appliquĂ© Ă  diffĂ©rents ensembles de faits. Dans ce cas, rien ne garantit que les rĂšgles concorderont avec tous les ensembles de faits possibles. Il est Ă©galement parfois acceptable que les rĂšgles elles-mĂȘmes ne soient pas cohĂ©rentes, par exemple si elles sont rĂ©digĂ©es par des experts diffĂ©rents.



Les campagnes les plus connues, permettent de formaliser la conclusion logique sous les définitions cycliques et les incohérences du programme sont la «sémantique durable» (sémantique du modÚle stable) et la «sémantique raisonnable» (la sémantique bien fondée).



La rĂšgle d'infĂ©rence avec une sĂ©mantique de modĂšle persistante est basĂ©e sur l'hypothĂšse que certains opĂ©rateurs de nĂ©gation dans un programme peuvent ĂȘtre ignorĂ©s s'ils ne sont pas cohĂ©rents avec le reste du programme. Puisqu'il peut y avoir plusieurs de ces sous-ensembles cohĂ©rents de l'ensemble initial de rĂšgles, il peut y avoir plusieurs solutions, respectivement. Par exemple, dans la dĂ©finition ci-dessus, l'infĂ©rence peut commencer par la premiĂšre rĂšgle ( p ← pas q ), Ă©carter le second ( q ← pas p ) et obtenir la solution {p, pas q} . Et puis faites de mĂȘme pour le second et obtenez {q, pas p} . La solution globale sera un ensemble combinĂ© de solutions alternatives. Par exemple, Ă  partir des rĂšgles:



person(alex)
alive(X) ←person(X)
male(X) ←person(X) AND NOT female(X)
female(X) ←person(X) AND NOT male(X)
      
      





nous pouvons afficher deux options de réponse: {personne (alex), vivant (alex), homme (alex)} et {personne (alex), vivant (alex), femme (alex)} .

La sĂ©mantique raisonnable commence avec les mĂȘmes hypothĂšses, mais cherche Ă  trouver une solution partielle gĂ©nĂ©rale qui satisfait tous les sous-ensembles consensuels alternatifs de rĂšgles. Une solution partielle signifie que les valeurs «vrai» ou «faux» ne seront affichĂ©es que pour une partie des faits, et les valeurs du reste resteront inconnues. Ainsi, dans la description des faits dans le programme, une logique Ă  deux valeurs est utilisĂ©e et, dans le processus d'infĂ©rence, Ă  trois valeurs. Pour les rĂšgles considĂ©rĂ©es ci-dessus, les significations de p et qsera inconnu. Mais, par exemple, pour



p ← not q
q ←not p
r ← s
s
      
      





nous pouvons en déduire avec certitude que r et s sont vrais, bien que p et q restent inconnus.



Par exemple, à partir de l'exemple alex, nous pourrions déduire {personne (alex), vivante (alex)} , tandis que les déclarations male (alex) et female {alex} restent inconnues.



En SQL, la nĂ©gation boolĂ©enne ( NOT ) et la vĂ©rification de la dĂ©rivabilitĂ© ( NOT EXISTS) sont sĂ©parĂ©s. Ces opĂ©rateurs s'appliquent Ă  diffĂ©rents types d'arguments: NOT inverse une valeur boolĂ©enne, et EXISTS / NOT EXISTS vĂ©rifie le rĂ©sultat d'une requĂȘte imbriquĂ©e pour la vacuitĂ©, donc cela n'a aucun sens de les combiner. La sĂ©mantique des opĂ©rateurs de nĂ©gation en SQL est trĂšs simple et n'est pas conçue pour fonctionner avec des requĂȘtes incohĂ©rentes rĂ©cursives ou complexes; avec une compĂ©tence SQL spĂ©ciale, la requĂȘte peut ĂȘtre envoyĂ©e Ă  une rĂ©cursivitĂ© infinie. Mais les constructions logiques complexes sont clairement en dehors de la portĂ©e traditionnelle de SQL, il n'a donc pas besoin d'une sĂ©mantique d'opĂ©rateur de nĂ©gation sophistiquĂ©e.



Essayons maintenant de comprendre la sémantique des opérateurs de négation du composant de modélisation du langage hybride conçu.



PremiĂšrement, le composant de modĂ©lisation est conçu pour intĂ©grer des sources de donnĂ©es disparates. Ils peuvent ĂȘtre trĂšs variĂ©s et ĂȘtre complets ou incomplets. Par consĂ©quent, des opĂ©rateurs de contrĂŽle de dĂ©rivabilitĂ© sont absolument nĂ©cessaires.



DeuxiĂšmement, la forme des concepts de composants de modĂ©lisation est beaucoup plus proche des requĂȘtes SQL que des rĂšgles de programmation logique. Le concept a Ă©galement une structure complexe, donc mĂ©langer dans un opĂ©rateur de nĂ©gation boolĂ©enne et vĂ©rifier la dĂ©rivabilitĂ© du concept n'a pas de sens. La nĂ©gation boolĂ©enne n'a de sens que pour s'appliquer aux attributs, aux variables et aux rĂ©sultats d'expression - ils peuvent ĂȘtre faux ou vrais. Il est plus difficile de l'appliquer Ă  un concept, il peut se composer de diffĂ©rents attributs et il n'est pas clair lequel d'entre eux devrait ĂȘtre responsable de la faussetĂ© ou de la vĂ©ritĂ© du concept dans son ensemble. Le concept peut ĂȘtre dĂ©rivĂ© des donnĂ©es initiales dans leur ensemble, et non de ses attributs individuels. Contrairement Ă  SQL, oĂč la structure des tables est fixe, la structure des concepts peut ĂȘtre flexible, un concept peut ne pas avoir du tout l'attribut requis dans sa composition,donc une vĂ©rification de l'existence de l'attribut est Ă©galement nĂ©cessaire.



Par consĂ©quent, il est judicieux d'introduire des opĂ©rateurs distincts pour chaque type de nĂ©gation rĂ©pertoriĂ© ci-dessus. La faussetĂ© des attributs peut ĂȘtre vĂ©rifiĂ©e Ă  l'aide de l'opĂ©rateur boolĂ©en NOT traditionnel , si un concept contient un attribut Ă  l'aide de la fonction intĂ©grĂ©e DEFINED et le rĂ©sultat de l'infĂ©rence du concept Ă  partir des donnĂ©es d'origine Ă  l'aide de la fonction EXISTS . Les trois opĂ©rateurs distincts sont plus prĂ©visibles, comprĂ©hensibles et plus faciles Ă  utiliser que l'opĂ©rateur complexe de nĂ©gation en tant que dĂ©faillance. Si nĂ©cessaire, ils peuvent ĂȘtre combinĂ©s en un seul opĂ©rateur d'une maniĂšre ou d'une autre qui convient Ă  chaque cas spĂ©cifique.



TroisiĂšmement, pour le moment, le composant de modĂ©lisation est considĂ©rĂ© comme un outil de crĂ©ation de petites ontologies au niveau des applications. Il est peu probable que son langage ait besoin d'une expressivitĂ© logique spĂ©ciale et de rĂšgles d'infĂ©rence sophistiquĂ©es capables de gĂ©rer les dĂ©finitions rĂ©cursives et les incohĂ©rences logiques du programme. Par consĂ©quent, la mise en Ɠuvre de rĂšgles d'infĂ©rence complexes basĂ©es sur la sĂ©mantique de modĂšles persistants ou une sĂ©mantique ancrĂ©e ne semble pas souhaitable, du moins Ă  ce stade. La rĂ©solution SLDNF classique devrait suffire.



Regardons maintenant quelques exemples.



Un concept peut recevoir une signification négative si certains de ses attributs ont une signification qui l'indique. La négation des attributs vous permet de trouver explicitement de telles entités:



concept unfinishedTask is task t where not t.completed
      
      





La fonction de vérification de l'ambiguïté d'un attribut sera pratique si les entités d'un concept peuvent avoir des structures différentes:



concept unassignedTask is task t where not defined(t.assignedTo) or empty(t.assignedTo)
      
      





La fonction de vérification de la déductibilité d'un concept est irremplaçable lorsque l'on travaille avec des définitions récursives et des structures hiérarchiques:



concept minimalElement is element greater 
where not exists(element lesser where greater.value > lesser.value)
      
      





Dans cet exemple, la vĂ©rification de l'existence d'un Ă©lĂ©ment plus petit est effectuĂ©e en tant que sous-requĂȘte. Je prĂ©vois d'Ă©tudier en dĂ©tail la crĂ©ation de requĂȘtes imbriquĂ©es dans la prochaine publication.



ÉlĂ©ments de logique d'ordre supĂ©rieur



Dans la logique du premier ordre, les variables ne peuvent correspondre qu'à des ensembles d'objets et n'apparaissent qu'aux positions des arguments des prédicats. Dans la logique d'ordre supérieur, ils peuvent également correspondre à des ensembles de relations et apparaßtre à la position des noms de prédicat. En d'autres termes, la logique du premier ordre permet d'affirmer qu'une relation est vraie pour tout ou partie des objets. Et la logique d'ordre supérieur consiste à décrire la relation entre les relations.



Par exemple, nous pouvons affirmer que certaines personnes sont des frùres, sƓurs, enfants ou parents, oncles ou tantes, etc.



Brother(John, Joe).
Son(John, Fred).
Uncle(John, Alex).
      
      





Mais pour faire une assertion de relation, dans la logique du premier ordre, nous devons lister toutes les instructions ci-dessus, en les combinant à l'aide de l'opération OR:



ⱯX,ⱯY(Brother(X, Y) OR Brother(Y, X) OR Son(X, Y) OR Son(Y, X) OR Uncle(X, Y) OR Uncle(Y, X) → Relative(X, Y)).
      
      





La logique du second ordre vous permet de faire une dĂ©claration sur d'autres dĂ©clarations. Par exemple, on pourrait dire directement que la relation entre frĂšres, sƓurs, parents et enfants, oncles et neveux est une relation de parentĂ©:



RelativeRel(Brother).
RelativeRel(Son).
RelativeRel(Uncle).
ⱯX,ⱯY(â±»R(RelativeRel(R) AND (R(X, Y) OR R(Y, X))) → Relative(X, Y)).
      
      





Nous soutenons que si pour chaque X et Y il existe une relation R qui est une relation entre des frĂšres et sƓurs RelativeRel , et que X et Y satisfont cette relation, alors X et Y sont des frĂšres et sƓurs. Les arguments de relation peuvent ĂȘtre d'autres relations et des variables peuvent ĂȘtre remplacĂ©es par des noms de relations.



La logique du troisiÚme ordre vous permet de construire des déclarations sur les déclarations sur les déclarations, et ainsi de suite, la logique d'ordre supérieur- sur les déclarations de tout niveau d'imbrication. La logique d'ordre supérieur est beaucoup plus expressive, mais aussi beaucoup plus complexe. En pratique, les systÚmes de programmation logique ne prennent en charge que certains de ses éléments, qui se limitent principalement à l'utilisation de variables et d'expressions arbitraires aux positions des prédicats.



Dans Prolog, les éléments d'une telle logique sont implémentés à l'aide de plusieurs méta-prédicats intégrés, dont les arguments sont d'autres prédicats. Le principal est l' appel de prédicat qui vous permet d'ajouter dynamiquement des prédicats à la liste cible de la rÚgle actuelle. Son premier argument est traité comme le but et le reste comme ses arguments. Prolog recherchera dans la base de connaissances les prédicats correspondant au premier argument et les ajoutera à la liste cible actuelle. Un exemple avec des proches ressemblerait à ceci:



brother(john, jack).
sister(mary, john).
relative_rel(brother).
relative_rel(sister).
relative(X, Y) :- relative_rel(R), (call(R, X, Y); call(R, Y, X)).
      
      





Prolog prend également en charge les prédicats findall (Template, Goal, Bag) , bagof (Template, Goal, Bag) , setof (Template, Goal, Set) , etc., qui vous permettent de trouver toutes les solutions d'objectif Goal qui correspondent au Template et unifier (lien) leur liste avec le résultat Sac (ou Set ). Prolog a des prédicats intégrés current_predicate , clause et autres pour rechercher des prédicats dans la base de connaissances. Vous pouvez également manipuler les prédicats et leurs attributs dans les bases de connaissances - les ajouter, les supprimer et les copier.



Le langage HiLog prend en charge la logique d'ordre supérieur au niveau de la syntaxe. Au lieu de méta-prédicats spéciaux, il permet d'utiliser des termes arbitraires (tels que des variables) directement à la position des noms de prédicat. La rÚgle de détermination des parents prendra la forme:



relative(X, Y) :- relative_rel(R), (R(X, Y); R(Y, X)).
      
      





Cette syntaxe est plus dĂ©clarative, concise, comprĂ©hensible et naturelle par rapport Ă  Prolog. Dans le mĂȘme temps, HiLog reste une variante syntaxique de Prolog, puisque toutes les constructions syntaxiques HiLog peuvent ĂȘtre transformĂ©es en expressions logiques de premier ordre Ă  l'aide des mĂ©ta-prĂ©dicats d' appel .



HiLog est considéré comme ayant une syntaxe d'ordre supérieur , mais une sémantique de premier ordre . Cela signifie que lors de la comparaison de variables qui représentent des rÚgles ou des fonctions, seuls leurs noms sont pris en compte, pas leur implémentation. Il existe également des langages qui prennent en charge la sémantique d'ordre supérieur, tels que λ-Prolog, qui permettent également d'implémenter des rÚgles et des fonctions dans le processus d'inférence. Mais cette logique et ses algorithmes d'inférence sont beaucoup plus compliqués.



Passons maintenant à la fonctionnalité logique d'ordre supérieur du composant de modélisation . Pour la plupart des tùches de méta-programmation pratiques, Prolog et HiLog devraient suffire. HiLog a une syntaxe plus naturelle, il est donc logique de la prendre comme base. Pour pouvoir utiliser des expressions arbitraires sur les positions des noms de concepts et de leurs attributs et pour les distinguer des variables, appels de fonctions et autres constructions, nous introduisons un opérateur spécial pour spécifier dynamiquement les noms :

< >







Il vous permet d'évaluer la valeur d'une expression et de l'utiliser comme nom de concept, alias ou nom d'attribut, selon le contexte. Si cet opérateur se place à la place du nom du concept dans la section FROM et que la valeur de son expression est définie, alors tous les concepts avec le nom spécifié seront trouvés et une recherche logique est effectuée pour eux:

concept someConcept ( 
 ) from conceptA a, <a.conceptName> b where 






Si la valeur de l'expression n'est pas définie, par exemple, l'expression est une variable booléenne non associée à la valeur , alors la procédure trouvera tous les concepts appropriés et associera la valeur de la variable à leurs noms:

concept someConcept is <$conceptName> where 






On peut dire que dans le contexte de la section FROM , l' opérateur pour spécifier un nom a une sémantique logique .

De plus, l'opĂ©rateur <> peut ĂȘtre utilisĂ© et les clauses WHERE Ă  la position d'un alias de concept ou d'un nom d'attribut:



concept someConcept ( 
 ) from conceptA a, conceptB b where conceptB.<a.foreignKey> = a.value ...
      
      





Les expressions de la clause WHERE sont déterministes, c'est-à-dire qu'elles n'utilisent pas la recherche logique pour trouver des valeurs inconnues pour leurs arguments. Cela signifie que l'expression conceptB. <A.foreignKey> = a.value ne sera évaluée qu'aprÚs avoir trouvé les entités du concept a et que ses attributs ForeignKey et value seront associés à des valeurs. Par conséquent, nous pouvons dire que dans le contexte de la clause FROM , l'instruction de nom a une sémantique fonctionnelle .



Considérons quelques applications possibles de la logique d'ordre supérieur.

L'exemple le plus Ă©vident oĂč la logique d'ordre supĂ©rieur conviendra est l'union sous un seul nom de tous les concepts qui satisfont Ă  certaines conditions. Par exemple, avoir certains attributs. Ainsi, le concept de point peut ĂȘtre considĂ©rĂ© comme tous les concepts qui incluent les coordonnĂ©es x et y :



concept point is <$anyConcept> a where defined(a.x) and defined(a.y)
      
      





La recherche boolĂ©enne liera la variable $ anyConcept avec tous les noms de concept dĂ©clarĂ©s (sauf bien sĂ»r lui-mĂȘme) qui ont des attributs de coordonnĂ©es.



Un exemple plus complexe serait de déclarer une relation générale qui s'applique à de nombreux concepts. Par exemple, une relation parent-enfant transitive entre des concepts:



relation ParentRel between <$conceptName> parent, <$conceptName> child
where defined(parent.id) and defined(child.parent) and (
parent.id = child.parent or exists(
	<$conceptName> intermediate where intermediate.parent = parent.id 
            and ParentRel(intermediate, child)	
))
      
      





La variable $ conceptName est utilisĂ©e dans les trois concepts de parent, enfant et intermĂ©diaire, ce qui signifie que leurs noms doivent ĂȘtre identiques.



La logique d'ordre supĂ©rieur ouvre des possibilitĂ©s de programmation gĂ©nĂ©rique dans le sens oĂč vous pouvez crĂ©er des concepts et des relations gĂ©nĂ©riques qui peuvent ĂȘtre appliquĂ©s Ă  une variĂ©tĂ© de concepts qui satisfont des conditions donnĂ©es sans ĂȘtre liĂ©s Ă  leurs noms spĂ©cifiques.



En outre, la substitution de nom dynamique sera pratique dans les cas oĂč les attributs d'un concept sont des rĂ©fĂ©rences aux noms d'autres concepts ou Ă  leurs attributs, ou lorsque les donnĂ©es source contiennent non seulement des faits, mais Ă©galement leur structure. Par exemple, les donnĂ©es source peuvent inclure une description des schĂ©mas de documents XML ou de tables dans une base de donnĂ©es. Les donnĂ©es d'origine peuvent Ă©galement inclure des informations supplĂ©mentaires sur les faits, par exemple, les types de donnĂ©es, les formats ou les valeurs par dĂ©faut, les conditions de validation ou certaines rĂšgles. En outre, les donnĂ©es initiales peuvent dĂ©crire le modĂšle de quelque chose, et le composant de modĂ©lisation sera responsable de la construction du mĂ©tamodĂšle. Travailler avec des textes en langage naturel suppose Ă©galement que les donnĂ©es source comprendront non seulement des dĂ©clarations, mais Ă©galement des dĂ©clarations sur des dĂ©clarations.Dans tous ces cas, la logique du premier ordre ne suffira pas et un langage plus expressif est nĂ©cessaire.



À titre d'exemple simple, considĂ©rons le cas oĂč les donnĂ©es incluent certains objets, ainsi que les rĂšgles de validation des attributs de ces objets en tant qu'entitĂ© distincte:



fact validationRule {objectName: “someObject”, attributeName: “someAttribute”, rule: function(value) {...}}
      
      





Les rĂ©sultats de la validation peuvent ĂȘtre dĂ©crits par le concept suivant:



concept validationRuleCheck (
	objectName = r.objectName,
	attributeName = r.attrName,
	result = r.rule(o.<r.attrName>)
) from validationRule r, <r.objectName> o 
where defined(o.<r.attrName>)
      
      





La logique d'ordre supĂ©rieur ouvre des possibilitĂ©s assez intĂ©ressantes pour la gĂ©nĂ©ralisation et la mĂ©ta-programmation. Nous n'avons pu considĂ©rer que son idĂ©e gĂ©nĂ©rale. Ce domaine est assez complexe et nĂ©cessite des recherches approfondies Ă  l'avenir. À la fois du point de vue du choix d'une conception pratique de la langue et des problĂšmes de ses performances.



conclusions



Dans le processus de travail sur le composant de modĂ©lisation, j'ai dĂ» faire face Ă  des problĂšmes assez spĂ©cifiques de programmation logique, tels que le travail avec des variables boolĂ©ennes, la sĂ©mantique de l'opĂ©rateur de nĂ©gation et des Ă©lĂ©ments de logique d'ordre supĂ©rieur. Si tout s'est avĂ©rĂ© assez simple avec des variables, il n'y a pas d'approche unique bien Ă©tablie pour la mise en Ɠuvre de l'opĂ©rateur de nĂ©gation et de la logique d'ordre supĂ©rieur, et il existe plusieurs solutions qui ont leurs propres caractĂ©ristiques, avantages et inconvĂ©nients.



J'ai essayĂ© de choisir une solution qui serait facile Ă  comprendre et pratique dans la pratique. J'ai prĂ©fĂ©rĂ© scinder l'opĂ©rateur de nĂ©gation monolithique comme un refus en trois opĂ©rateurs distincts de nĂ©gation boolĂ©enne, vĂ©rifiant la dĂ©ductibilitĂ© d'un concept et l'existence d'un attribut dans un objet. Si nĂ©cessaire, ces trois opĂ©rateurs peuvent ĂȘtre combinĂ©s pour obtenir la sĂ©mantique de nĂ©gation requise pour chaque cas spĂ©cifique. Pour la rĂšgle de nĂ©gation de l'infĂ©rence, j'ai dĂ©cidĂ© de prendre la rĂ©solution SLDNF standard comme base. Bien qu'il ne soit pas capable de traiter des dĂ©clarations rĂ©cursives incohĂ©rentes, il est beaucoup plus facile Ă  comprendre et Ă  implĂ©menter que des alternatives plus sophistiquĂ©es telles que la sĂ©mantique de modĂšle persistante ou la sĂ©mantique raisonnĂ©e.



Une logique d'ordre supĂ©rieur est nĂ©cessaire pour la gĂ©nĂ©ralisation et la mĂ©ta-programmation. Par programmation gĂ©nĂ©rique, j'entends la capacitĂ© de construire de nouveaux concepts Ă  partir d'une grande variĂ©tĂ© de concepts enfants sans ĂȘtre liĂ© Ă  des noms spĂ©cifiques de ces derniers. Par mĂ©ta-programmation, j'entends la capacitĂ© de dĂ©crire la structure de certains concepts Ă  l'aide d'autres concepts. J'ai dĂ©cidĂ© de prendre le langage HiLog comme modĂšle pour les Ă©lĂ©ments logiques d'ordre supĂ©rieur du composant de modĂ©lisation. Il a une syntaxe flexible et pratique qui vous permet d'utiliser des variables aux positions des noms de prĂ©dicat, ainsi qu'une sĂ©mantique simple et claire.



Le prochain article que je compte consacrer Ă  l'emprunt dans le monde de SQL: requĂȘtes imbriquĂ©es et agrĂ©gations. Je parlerai Ă©galement d'un autre type de concepts qui n'utilise pas l'infĂ©rence, mais Ă  la place, les entitĂ©s sont directement gĂ©nĂ©rĂ©es Ă  l'aide d'une fonction donnĂ©e. Et comment vous pouvez l'utiliser pour convertir des tables, des tableaux et des tableaux associatifs au format objet et les inclure dans le processus d'infĂ©rence logique (par analogie avec l'opĂ©ration SQL UNNEST, qui convertit les tableaux au format de table).



Le texte intégral en style scientifique en anglais est disponible ici .



Liens vers les publications précédentes:



Conception d'un langage de programmation multi-paradigme. Partie 1 - À quoi ça sert?

Nous concevons un langage de programmation multi-paradigme. Partie 2 - Comparaison des modĂšles de construction en PL / SQL, LINQ et GraphQL Nous

concevons un langage de programmation multi-paradigmes. Partie 3 - Vue d'ensemble des langages de représentation des connaissances Nous

concevons un langage de programmation multi-paradigmes. Partie 4 - Constructions de base du langage de modélisation



All Articles