Оценка математических выражений с использованием Lua - PullRequest
5 голосов
/ 21 июля 2009

В моем предыдущем вопросе Я искал способ выражения сложных математических выражений в C, большинство предложений требовало реализации какого-то синтаксического анализатора.

Однако один ответ , предложил использовать Lua для оценки выражения. Мне интересен этот подход, но я ничего не знаю о Луа.

Может ли кто-нибудь с опытом в Lua пролить свет?

В частности, я хотел бы знать, Какой API, если таковой имеется, предоставляет Lua, который может оценивать математические выражения, переданные в виде строки? Если нет API для такой вещи, может быть кто-то может пролить свет на связанный ответ, как это казалось вроде хороший подход :) 1013 *

Спасибо

Тип выражения, которое я хотел бы оценить, задается пользовательским вводом, таким как

y = x ^ 2 + 1 / x - cos (x)

оценить y для диапазона значений х

Ответы [ 5 ]

5 голосов
/ 21 июля 2009

Очень просто установить экземпляр интерпретатора Lua и передать ему выражения для оценки, возвращая функцию для вызова, которая оценивает выражение. Вы даже можете позволить пользователю иметь переменные ...

Вот пример кода, который я подготовил и отредактировал в другой ответ. Возможно, в любом случае лучше задать вопрос с тегом Lua, поэтому я добавлю его и здесь. Я скомпилировал это и попробовал несколько раз, но, конечно, нельзя доверять производственному коду без некоторого внимания к обработке ошибок и так далее. Все обычные предостережения применяются здесь.

Я скомпилировал и протестировал это в Windows, используя Lua 5.1.4 из Lua для Windows . На других платформах вам нужно будет найти Lua из вашего обычного источника или с www.lua.org.

Обновление: В этом примере используются простые и прямые методы, позволяющие скрыть всю мощь и сложность Lua API за максимально простым интерфейсом. Это, вероятно, полезно как есть, но может быть улучшено несколькими способами.

Я бы посоветовал читателям заглянуть в гораздо более готовую к работе библиотеку ae от lhf для кода, использующего преимущества API, чтобы избежать некоторых быстрых и грязных манипуляций со строками. Я использовал Его библиотека также продвигает математическую библиотеку в глобальное пространство имен, так что пользователь может сказать sin(x) или 2 * pi, не произнося math.sin и так далее.

Публичный интерфейс к LE

Вот файл le.h:

/* Public API for the LE library.
 */
int le_init();
int le_loadexpr(char *expr, char **pmsg);
double le_eval(int cookie, char **pmsg);
void le_unref(int cookie);
void le_setvar(char *name, double value);
double le_getvar(char *name);

Пример кода с использованием LE

Вот файл t-le.c, демонстрирующий простое использование этой библиотеки. Он принимает единственный аргумент командной строки, загружает его как выражение и оценивает его с помощью глобальной переменной x, изменяющейся от 0.0 до 1.0 за 11 шагов:

#include <stdio.h>
#include "le.h"

int main(int argc, char **argv)
{
    int cookie;
    int i;
    char *msg = NULL;

    if (!le_init()) {
    printf("can't init LE\n");
    return 1;
    }
    if (argc<2) {
    printf("Usage: t-le \"expression\"\n");
    return 1;
    }
    cookie = le_loadexpr(argv[1], &msg);
    if (msg) {
    printf("can't load: %s\n", msg);
    free(msg);
    return 1;
    }
    printf("  x    %s\n"
       "------ --------\n", argv[1]);
    for (i=0; i<11; ++i) {
    double x = i/10.;
    double y;

    le_setvar("x",x);
    y = le_eval(cookie, &msg);
    if (msg) {
        printf("can't eval: %s\n", msg);
        free(msg);
        return 1;
    }
    printf("%6.2f %.3f\n", x,y);
    }
}

Вот некоторый вывод из t-le:

E:...>t-le "math.sin(math.pi * x)"
  x    math.sin(math.pi * x)
------ --------
  0.00 0.000
  0.10 0.309
  0.20 0.588
  0.30 0.809
  0.40 0.951
  0.50 1.000
  0.60 0.951
  0.70 0.809
  0.80 0.588
  0.90 0.309
  1.00 0.000

E:...>

Реализация LE

Вот le.c, реализующий оценщик Lua Expression:

#include <lua.h>
#include <lauxlib.h>

#include <stdlib.h>
#include <string.h>

static lua_State *L = NULL;

/* Initialize the LE library by creating a Lua state.
 *
 * The new Lua interpreter state has the "usual" standard libraries
 * open.
 */
int le_init()
{
    L = luaL_newstate();
    if (L) 
    luaL_openlibs(L);
    return !!L;
}

/* Load an expression, returning a cookie that can be used later to
 * select this expression for evaluation by le_eval(). Note that
 * le_unref() must eventually be called to free the expression.
 *
 * The cookie is a lua_ref() reference to a function that evaluates the
 * expression when called. Any variables in the expression are assumed
 * to refer to the global environment, which is _G in the interpreter.
 * A refinement might be to isolate the function envioronment from the
 * globals.
 *
 * The implementation rewrites the expr as "return "..expr so that the
 * anonymous function actually produced by lua_load() looks like:
 *
 *     function() return expr end
 *
 *
 * If there is an error and the pmsg parameter is non-NULL, the char *
 * it points to is filled with an error message. The message is
 * allocated by strdup() so the caller is responsible for freeing the
 * storage.
 * 
 * Returns a valid cookie or the constant LUA_NOREF (-2).
 */
int le_loadexpr(char *expr, char **pmsg)
{
    int err;
    char *buf;

    if (!L) {
    if (pmsg)
        *pmsg = strdup("LE library not initialized");
    return LUA_NOREF;
    }
    buf = malloc(strlen(expr)+8);
    if (!buf) {
    if (pmsg)
        *pmsg = strdup("Insufficient memory");
    return LUA_NOREF;
    }
    strcpy(buf, "return ");
    strcat(buf, expr);
    err = luaL_loadstring(L,buf);
    free(buf);
    if (err) {
    if (pmsg)
        *pmsg = strdup(lua_tostring(L,-1));
    lua_pop(L,1);
    return LUA_NOREF;
    }
    if (pmsg)
    *pmsg = NULL;
    return luaL_ref(L, LUA_REGISTRYINDEX);
}

/* Evaluate the loaded expression.
 * 
 * If there is an error and the pmsg parameter is non-NULL, the char *
 * it points to is filled with an error message. The message is
 * allocated by strdup() so the caller is responsible for freeing the
 * storage.
 * 
 * Returns the result or 0 on error.
 */
double le_eval(int cookie, char **pmsg)
{
    int err;
    double ret;

    if (!L) {
    if (pmsg)
        *pmsg = strdup("LE library not initialized");
    return 0;
    }
    lua_rawgeti(L, LUA_REGISTRYINDEX, cookie);
    err = lua_pcall(L,0,1,0);
    if (err) {
    if (pmsg)
        *pmsg = strdup(lua_tostring(L,-1));
    lua_pop(L,1);
    return 0;
    }
    if (pmsg)
    *pmsg = NULL;
    ret = (double)lua_tonumber(L,-1);
    lua_pop(L,1);
    return ret;
}


/* Free the loaded expression.
 */
void le_unref(int cookie)
{
    if (!L)
    return;
    luaL_unref(L, LUA_REGISTRYINDEX, cookie);    
}

/* Set a variable for use in an expression.
 */
void le_setvar(char *name, double value)
{
    if (!L)
    return;
    lua_pushnumber(L,value);
    lua_setglobal(L,name);
}

/* Retrieve the current value of a variable.
 */
double le_getvar(char *name)
{
    double ret;

    if (!L)
    return 0;
    lua_getglobal(L,name);
    ret = (double)lua_tonumber(L,-1);
    lua_pop(L,1);
    return ret;
}

Примечания

Приведенный выше пример состоит из 189 строк кода, включая разбрызгивание комментариев, пустые строки и демонстрацию. Неплохо для быстрого оценщика функций, который знает, как вычислять разумно произвольные выражения для одной переменной, и имеет в своем распоряжении богатую библиотеку стандартных математических функций в своем вызове и вызове.

У вас под полным языком Тьюринга есть простое расширение, позволяющее пользователю определять завершенные функции, а также оценивать простые выражения.

4 голосов
/ 21 июля 2009

Поскольку вы ленивы, как и большинство программистов, вот ссылка на простой пример, который вы можете использовать для анализа некоторого произвольного кода с использованием Lua . Оттуда должно быть просто создать ваш анализатор выражений.

2 голосов
/ 06 февраля 2015

Это для пользователей Lua, которые ищут эквивалент Lua для "eval".

Волшебное слово раньше было loadstring, но теперь, начиная с Lua 5.2, это обновленная версия load .

 i=0
 f = load("i = i + 1") -- f is a function
 f() ; print(i) -- will produce 1
 f() ; print(i) -- will produce 2

Другой пример, который предоставляет значение:

f=load('return 2+3')
print(f()) -- print 5

В качестве быстрого и грязного способа вы можете рассмотреть следующий эквивалент eval (s), где s - строка для оценки:

load(s)()

Как всегда, по возможности следует избегать механизмов eval, так как они дороги и создают код, который трудно читать. Я лично использую этот механизм с LuaTex / LuaLatex для выполнения математических операций в Latex.

1 голос
/ 21 июля 2009

Документация Lua содержит раздел под названием Интерфейс прикладного программирования , в котором описано, как вызывать Lua из вашей программы на Си. Документация по Lua очень хорошая, и вы даже сможете найти пример того, что вы хотите сделать.

Это большой мир, так что, выбираете ли вы свое собственное решение для анализа или встраиваемого интерпретатора, такого как Lua, вам придется поработать!

0 голосов
/ 22 апреля 2017
function calc(operation)
    return load("return " .. operation)()
end
...