Hola queridos lectores.
Las expresiones regulares son algo muy conocido que se usa en varios proyectos, con mayor frecuencia para casos no muy complicados de análisis de textos estructurados. A primera vista, participar en una tarea tan ligeramente diferente como la síntesis inversa de los modelos de programa (cuando hay un código de programa generado automáticamente por algún sistema de acuerdo con algún modelo de bloque del problema que se está resolviendo, y es necesario recrear el modelo original usando este código), así como sintetizar modelos de programa usando texto En la descripción de la tarea, me encontré con el problema de analizar textos, o más bien, identificar fragmentos de texto para algunas plantillas personalizadas. Quería obtener una solución bastante simple y flexible (personalizable). Las expresiones regulares, sobre la marcha, no lo parecían, porque incluso en una tarea tan simple como verificar una palabra en un diccionario, desafortunadamente, requería una lista cuidadosa de todas las opciones en esta expresión. Y no construyeron un árbol de análisis. Sin embargo, claramente podrían mejorarse. Esto será discutido.
Entonces, se establecieron las
siguientes tareas :
- La expresión regular debería ser capaz de producir un árbol de análisis. Es necesario implementar medios estándar de acceso a este árbol.
- La expresión regular debería poder incluir verificaciones en los fragmentos encontrados en el diccionario (correspondencia exacta o no estricta según Levenshtein), así como verificaciones más complejas en varias tablas al mismo tiempo.
- Además de los controles simples (enumerados anteriormente), me gustaría tener más controles difusos, por ejemplo, la compatibilidad de palabras y expresiones, una red neuronal
Árbol Parse.
En las expresiones regulares, los fragmentos analizados se identifican por el número de paréntesis. Esto, por decirlo suavemente, es inconveniente, por lo que se tomó una decisión sobre la posibilidad de nombrar paréntesis. Por cierto, son estos nombres los que deberían aparecer en el árbol de análisis. La sintaxis fue elegida simple:
(_)->{_}
Si después de paréntesis en la expresión original había algún operador (*, +, etc.), entonces él "se movió" detrás de la llave derecha. Por ejemplo:
(\w+\s)->{A}+
Nada impide los nombres y paréntesis, por ejemplo:
((\w+)->{ID}\s)->{A}+
En el último ejemplo, la expresión regular modificada generará un árbol de análisis con alguna raíz condicional, en el siguiente nivel hay instancias de A (puede haber más de uno), en el siguiente nivel hay valores de ID. Es conveniente acceder a dicho árbol utilizando XPath, que he implementado, por ejemplo, tal solicitud es posible:
//A[2]/ID[text()!=""]/text()
Verificación de palabras en diccionarios, tablas y una red neuronal simple
Analizar una expresión regular es muy similar a analizar una expresión lógica simple, por ejemplo, en el lenguaje Prolog. Esto lleva a la idea de que fragmentos similares a Prolog, que serán:
a) cadenas de varios controles. Esto no es difícil, especialmente porque las variables ya han aparecido (en forma de paréntesis con nombre);
b) o reposición de tablas / diccionarios con fragmentos detectados;
c) o excepciones de las tablas / diccionarios de los fragmentos detectados;
d) o un equipo de creación / formación de redes neuronales.
La sintaxis general aquí sería:
(_)_=>{1,2,...}
símbolo_operación depende de la operación: verificación (?), reposición (+), exclusión (-), creación / capacitación (*). En cuanto a los predicados, se indica el nombre del predicado (estándar o el suyo propio) y una lista de sus parámetros entre paréntesis. Los parámetros pueden ser constantes, variables de entrada, variables de salida (prefijadas con un signo "$"), un signo de un valor arbitrario "_", una referencia al valor actual del
fragmento expresión_ (carácter único "$"). El análisis de dicha expresión se considera exitoso si todos los predicados en la cadena son exitosos.
Por ejemplo, la expresión:
([--]+)->{V1}\s+([--]+)->{V2}()?=>{check(V1},check(V2),correlate(V1,V2)}
selecciona dos palabras consecutivas en ruso y las pone en las variables V1 y V2, y luego verifica estas palabras con la verificación de predicados (esto puede ser una simple verificación en la tabla) y, en conclusión, con la correlación de predicados (también puede haber una verificación en la tabla). El tipo de verificación (estricta o no estricta) está determinada por la especificación del predicado.
Si la tabla de correlación no contiene las palabras en sí, sino algunos códigos de palabras y una característica de su compatibilidad, entonces puede haber una expresión de este tipo:
()->{C1}()->{C2}([--]+)->{check($,$C1}\s+([--]+)->{V2}()?=>{check(V2,$C2),correlate(C1,C2,1)}
Aquí, se introducen por primera vez dos variables, C1 y C2. El predicado correlacionado también puede ser una red neuronal con dos entradas (códigos de palabras) y una salida (compatibilidad), que se entrena de acuerdo con un conjunto predefinido o ensamblado dinámicamente (durante la operación de expresión regular).
Queda por agregar que las directivas especiales paralelas y secuenciales también se pueden especificar como predicados, que, respectivamente, incluyen y calculan el paralelismo de ejecución en la cadena de predicados (que se convierte en el árbol de dependencia de predicados, según el cual está paralelo). Además, puede habilitar un modo especial en el que se intente predecir dinámicamente el tiempo de ejecución de predicados individuales y decidir si la concurrencia será efectiva o viceversa, solo traerá costos adicionales.
Conclusión
Todas las modificaciones de expresiones regulares descritas se implementan en el prototipo (modificación del estándar regexpr.pas) en Free Pascal.
Espero que estas ideas sean útiles para alguien.
Ahora, usando tales expresiones lógicas regulares + programación en un Prolog puro, fue posible, en primer lugar, escribir un complemento en el sistema de generación de programas que restaura el modelo de programa original a partir del código generado previamente, y en segundo lugar, crear elementos del lenguaje natural para este sistema interfaz (el enunciado del problema se adopta en un idioma ruso muy simplificado y se formula un modelo del problema, según el cual el programa ya está generado).