Expressions régulières + programmation logique. Quel est le résultat?

Bonjour chers lecteurs.

Les expressions régulières sont une chose bien connue qui est utilisée dans divers projets, le plus souvent pour des cas pas très compliqués d'analyse de textes structurés. À première vue, être engagé dans une tâche légèrement différente comme la synthèse inverse de modèles de programme (lorsqu'il y a du code de programme généré automatiquement par un système selon un modèle de bloc du problème résolu et qu'il est nécessaire de recréer le modèle d'origine en utilisant ce code), ainsi que de synthétiser des modèles de programme en utilisant du texte la description de la tâche, je suis tombé sur le problème de l'analyse des textes, ou plutôt, l'identification de fragments de texte à certains modèles personnalisés. Je voulais obtenir une solution assez simple et flexible (personnalisable). Les expressions régulières, à la volée, ne semblaient pas ainsi, car même dans une tâche aussi simple que la vérification d'un mot dans un dictionnaire, malheureusement, il fallait une liste minutieuse de toutes les options de cette expression. Et ils n'ont pas construit d'arbre d'analyse. Cependant, ils pourraient clairement être améliorés. Cela sera discuté.

Ainsi, les tâches suivantes ont été définies:

  1. L'expression régulière doit pouvoir produire un arbre d'analyse. Il est nécessaire de mettre en place des moyens d'accès standard à cet arbre.
  2. L'expression régulière devrait pouvoir inclure des vérifications sur les fragments trouvés dans le dictionnaire (correspondance exacte ou non stricte selon Levenshtein), ainsi que des vérifications plus complexes sur plusieurs tables en même temps.
  3. En plus des vérifications simples (énumérées ci-dessus), j'aimerais avoir des vérifications plus floues, par exemple, la compatibilité des mots et des expressions, un réseau de neurones

Analyser l'arbre.


Dans les expressions régulières, les fragments analysés sont identifiés par le nombre de crochets. Pour dire les choses légèrement, cela n'est pas pratique, donc une décision a été prise quant à la possibilité de nommer les crochets. Soit dit en passant, ce sont ces noms qui devraient apparaître dans l'arbre d'analyse. La syntaxe a été choisie simple:

(_)->{_} 

Si après des parenthèses dans l'expression d'origine, il y avait un opérateur (*, +, etc.), alors il "se déplaçait" derrière l'accolade droite. Par exemple:

 (\w+\s)->{A}+ 

Rien n'empêche de nommer et de parenthèses, par exemple:

 ((\w+)->{ID}\s)->{A}+ 

Dans le dernier exemple, l'expression régulière modifiée générera un arbre d'analyse avec une racine conditionnelle, au niveau suivant il y a des instances de A (il peut y en avoir plusieurs), au niveau suivant il y a des valeurs ID. Il est pratique d'accéder à une telle arborescence en utilisant XPath, que j'ai implémenté, par exemple, une telle demande est possible:

 //A[2]/ID[text()!=""]/text() 

Vérification de mots sur des dictionnaires, des tableaux et un réseau neuronal simple


L'analyse d'une expression régulière est très similaire à l'analyse d'une expression logique simple, par exemple, dans le langage Prolog. Cela conduit à l'idée que des fragments de type Prolog, qui seront:

a) chaînes de contrôles divers. Ce n'est pas difficile, d'autant plus que les variables sont déjà apparues (sous forme de crochets nommés);
b) ou reconstitution de tableaux / dictionnaires avec des fragments détectés;
c) ou des exceptions aux tables / dictionnaires des fragments détectés;
d) ou une équipe de création / formation de réseaux neuronaux.

La syntaxe générale ici serait:

 (_)_=>{1,2,...} 

operation_symbol dépend de l'opération: vérification (?), réapprovisionnement (+), exclusion (-), création / formation (*). Quant aux prédicats, le nom du prédicat (standard ou le vôtre) et une liste de ses paramètres entre parenthèses sont indiqués. Les paramètres peuvent être des constantes, des variables d'entrée, des variables de sortie (préfixées d'un signe "$"), un signe d'une valeur arbitraire "_", une référence à la valeur actuelle du fragment expression_ (caractère unique "$"). L'analyse d'une telle expression est considérée comme réussie si tous les prédicats de la chaîne réussissent.

Par exemple, l'expression:

 ([--]+)->{V1}\s+([--]+)->{V2}()?=>{check(V1},check(V2),correlate(V1,V2)} 

sélectionne deux mots consécutifs en russe et les place dans les variables V1 et V2, puis vérifie ces mots avec la vérification de prédicat (cela peut être une simple vérification sur la table) et, en conclusion, avec le corrélat de prédicat (il peut également y avoir une vérification sur la table). Le type de vérification (strict ou non strict) est déterminé par la spécification du prédicat.

Si le tableau de corrélation ne contient pas les mots eux-mêmes, mais certains codes de mots et une caractéristique de leur compatibilité, alors il peut y avoir une telle expression:

 ()->{C1}()->{C2}([--]+)->{check($,$C1}\s+([--]+)->{V2}()?=>{check(V2,$C2),correlate(C1,C2,1)} 

Ici, deux variables sont d'abord introduites, C1 et C2. Le prédicat corrélé peut également être un réseau neuronal avec deux entrées (codes de mots) et une sortie (compatibilité), qui est formé selon un ensemble prédéfini ou assemblé dynamiquement (pendant l'opération d'expression régulière).

Il reste à ajouter que des directives spéciales parallèles et séquentielles peuvent également être spécifiées en tant que prédicats, qui, respectivement, incluent et calculent le parallélisme d'exécution dans la chaîne de prédicats (qui est convertie en l'arborescence de dépendances des prédicats, selon laquelle elle est parallélisée). De plus, vous pouvez activer un mode spécial dans lequel une tentative est faite pour prédire dynamiquement le temps d'exécution de prédicats individuels et décider si la concurrence sera effective, ou vice versa, n'entraînera que des coûts supplémentaires.

Conclusion


Toutes les modifications d'expressions régulières décrites sont implémentées dans le prototype (modification du regexpr.pas standard) sur Free Pascal.

J'espère que ces idées seront utiles à quelqu'un.

Maintenant, en utilisant de telles expressions logiques régulières + programmation sur un Prolog pur, il était possible, premièrement, d'écrire un ajout au système de génération de programme qui restaure le modèle de programme d'origine à partir du code précédemment généré par celui-ci, et deuxièmement, de créer des éléments du langage naturel pour ce système interface (l'énoncé du problème est adopté dans une langue russe très simplifiée et un modèle du problème est formulé dessus, selon lequel le programme est déjà généré).

Source: https://habr.com/ru/post/fr415797/


All Articles