2011-07-15 38 views
10

Tengo que evaluar un gran número de expresiones que contienen variables y estoy pensando en escribir un pequeño intérprete personalizado para mantener la compilación rápida y pequeña. Sin embargo, no tengo experiencia con este tema y tengo algunas preguntas.Intérprete personalizado para expresiones matemáticas

Digamos que tenemos un archivo con expresiones matemáticas y un conjunto limitado de objetos. El archivo podría quedar así:

expr[x,y,z] = 2*x*y + x^2 + 28/14*z*(x*y^2 + 15*z) + ... 

me gustaría analizar esto de alguna manera para que pueda evaluar las expresiones numéricamente en mi solicitud simplemente llamando a una función expr(float x, float y, float z). El número de parámetros no debería ser fijo (EDITAR: cada expresión tendría su propia definición con el número apropiado de parámetros o aceptaría una matriz) y se debería permitir que la anidación de paréntesis mantenga los archivos de entrada razonablemente pequeños.

Dado que las expresiones son todas de tipo polinomial, puedo pensar en cómo debería ser la estructura de datos, pero el análisis parece difícil. Ya he encontrado algunas respuestas a preguntas algo similares aquí en SO, por ejemplo usando Lua.

La pregunta más importante, sin embargo, es cuál sería la penalización del rendimiento al crear y llamar a esos objetos en comparación con la compilación directa de estas expresiones del código C generado automáticamente.

¡Gracias de antemano!

EDITAR: Tenga en cuenta el ejemplo de expr() anterior solo como tal. Supongo que la mejor manera sería tener objetos de una clase con plantilla que contenga coeficientes y potencias de las variables en matrices dispersas.

+0

"llamar a una función expr (float x, float y, float z). El número de parámetros no debe ser corregido" - hay un problema allí, entonces, ya que el número de parámetros en una C o la llamada a la función C++ * es * fija. Incluso con varargs, donde el destinatario puede hacer frente a diferentes números, la persona que llama tiene que fijar el número en el momento de la compilación. Probablemente necesites pasar una matriz en su lugar. –

+0

@Steve Jessop: corregido, estoy enterado de esto. – bbtrb

+0

¿Por qué no simplemente escribe la expresión de su función como una función C y la compila/ejecuta? –

Respuesta

6

El rendimiento es un poco de un problema de longitud de una cuerda. Los lenguajes interpretados casi siempre son más lentos que el código C compilado para evaluar expresiones aritméticas. Pero no es que muchos programas pasen la mayor parte de su tiempo haciendo aritmética, por lo que la mayoría de las veces eso no importa. También hace una diferencia si analiza la expresión cada vez que la evalúa o (como parece más probable a partir de lo que dice), la analiza en alguna forma intermedia.

Es imposible saber a partir de lo que ha dicho, si le importará o qué tan rápido escribirá un intérprete, pero no esperaría que sea mejor que 10 veces más lento, en cuanto al tiempo dedicado a evaluar las expresiones se refiere. Los primeros intentos de interpretación han sido mucho peores.

En cuanto a esa forma intermedia, el lugar habitual para comenzar es usar el algoritmo de Dijkstra "patio de maniobras" para convertir las expresiones de infijo a una forma polaca inversa. Eso le da una secuencia de "símbolos", "códigos de bytes", llámalos como prefiera, y es fácil escribir un evaluador de expresiones para esa forma: cada operador simplemente saca sus operandos de una pila, realiza el op, luego empuja el resultado en la pila, hasta que el valor final de la expresión sea lo único que quede al final. Los literales numéricos y los nombres de variables son simplemente como "operadores" que no revelan ningún operando, y presionan su valor.

[Editar: dependiendo de quiénes sean sus usuarios, podría ser factible que su programa tome ese archivo de texto, genere un programa C a partir de él, ejecute un compilador y luego ejecute el programa resultante (o, abra y llame al dll resultante). Obviamente, eso depende de muchas cosas específicas del sistema (un compilador está siendo instalado, por ejemplo), y las expresiones deberían evaluarse lo suficiente como para superar la sobrecarga de la compilación.]

+0

Gracias por su contribución. Así que este es el punto: las expresiones consisten a veces en miles de términos (con hasta 15 parámetros) y deben evaluarse una y otra vez para diferentes valores de parámetros. El análisis debe hacerse solo una vez. Al pensar en tu sugerencia de presionar y pop desde una pila, supongo que mi pregunta es: ¿es esta la manera correcta de hacerlo si las expresiones son siempre polinomiales? ¿Habría una penalización de rendimiento (aparte del análisis inicial) del todo si intentara implementar esto como en mi segundo EDIT? – bbtrb

+0

@bbrtb: lo que se describe en la segunda edición suele ser algo más lento que la misma expresión escrita en C. En primer lugar porque hay una sobrecarga para acceder a la estructura dispersa que le dice los poderes y coeficientes, en segundo lugar porque los compiladores de C vienen con kick-ass optimizadores. Ejemplo realmente simple, si su polinomio contiene sub expresiones repetidas, entonces un optimizador C probablemente las detectará y evitará trabajos duplicados, pero el evaluador que escriba que tome una matriz de coeficientes tendrá dificultades para ser tan inteligente. –

+0

Ya veo, todavía parece que vale la pena intentarlo. Lo que podría hacer para optimizar esto hasta cierto punto es anidar los objetos polinomiales. La entrada que tengo es como en mi publicación, por lo que ya está simplificada ya que se genera con Mathematica. – bbtrb

0

Hay un ejemplo simple en el Bison Manual.

+1

Esto obtiene OP un analizador que evalúa la expresión a medida que analiza. Si OP tiene que volver a evaluar la expresión, usar esta técnica obligará a OP a volver a analizar.En otros hilos de comentarios, dijo que no quería hacer eso. –

+0

Si el OP quiere compilar la expresión en algún tipo de árbol de objetos, el ejemplo proporciona un punto de partida – doron

1

Ha declarado el problema como "expresiones complejas grandes" y le preocupan las penalizaciones de rendimiento. Entonces deberías considerar compilarlos, no interpretarlos. (los buenos intérpretes son 10 veces más lentos que el código compilado como regla general, los intérpretes pésimos/ad hoc tienden a ser significativamente peores).

La ruta habitual para esto es "compilar" las expresiones de alguna manera, lo que implica analizadores de construcción, generadores de código, optimizaciones, etc.

compiladores de C ya hacer todo esto. Así que creo que sería mucho mejor traducir estas expresiones a C. La compilación es fácil y la ejecución será increíblemente rápida en comparación con cualquier cosa que se pueda esperar como intérprete. Eso también se puede hacer usando un analizador sintáctico y una traducción dirigida por sintaxis mucho más simple.

Pero Si todas estas expresiones son producidas por Mathematica, tendrán una estructura bastante estándar pero no complicada. En este caso, supongo que podría escribir un traductor basado en expresiones regulares que pudiera asignar los formularios de Mathematica a las funciones C sin muchos problemas; Perl sería ideal para esto. Esto le brinda una solución fácil de implementar y muy rápida.

Por lo que vale, creo que Mathematica tiene una opción para convertir expresiones de Mathematica directamente en C. Parece que eso también valdría la pena echarle un vistazo.

+0

Gracias por su contribución. El problema es que tengo 100 de MB de expresiones que se evaluarán muchas veces para diferentes parámetros, por lo que la compilación directa no es el camino a seguir (he intentado esto y una de mis preguntas de SO tratadas con este problema en un proyecto relacionado) . Sé que es difícil de estimar (y no tuve tiempo de trabajar en esto todavía), pero ¿la regla empírica más lenta de 10 veces también sería válida para una evaluación como mencioné en mi segunda edición (iteración repetida sobre matrices dispersas)? – bbtrb

+0

Usted dice que la compilación directa no es el camino a seguir, pero no está dando ninguna buena razón aparte de una vaga referencia a otro proyecto que obviamente no conozco. Si codifica un intérprete, tendrá un intérprete. A menos que lo codifique muy bien (me da la impresión de que esta no es una habilidad específica que usted cree que tiene), lo hará peor que el habitual 10x. –

+0

... miré tus otras preguntas de SO; el más obviamente relevante fue "mi archivo de objeto es> 2 Gb" pero pareces haberlo superado. Entonces, ¿cuál es el problema con la otra solución? Estoy de acuerdo con el tono de la mayoría de las respuestas; Creo que deberías reconsiderar cómo obtuviste un gran número de funciones tan grandes en primer lugar. Nadie escribió todo ese código; se está generando de alguna manera. Creo que debe volver a la forma en que se generó y reconsiderar por qué se genera de esa manera; claramente, está dificultando la resolución del problema. –

Cuestiones relacionadas