Оценка математических выражений с помощью Lua

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

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

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

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

Спасибо

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

у = х ^ 2 + 1 / х - соз (х)

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


person hhafez    schedule 20.07.2009    source источник
comment
Что вы хотите узнать? Синтаксис? Возможно ли это? Представление? Пожалуйста, дополните.   -  person a_m0d    schedule 21.07.2009
comment
см. мою правку жирным шрифтом;) Спасибо за комментарий   -  person hhafez    schedule 21.07.2009
comment
Почему бы тебе просто не сделать это длинным (но легким) способом?   -  person akway    schedule 21.07.2009
comment
какой длинный, но легкий путь?   -  person hhafez    schedule 21.07.2009


Ответы (5)


Просто настроить экземпляр интерпретатора 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 строк кода, включая множество комментариев, пустые строки и демонстрацию. Неплохо для быстрого оценщика функций, который знает, как оценивать достаточно произвольные выражения одной переменной, и имеет богатая библиотека стандартных математических функций в любое время.

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

person RBerteig    schedule 21.07.2009
comment
@lhf, ничего особенного, конечно ... Я видел это в вашем списке полезных привязок и утилит в прошлом, но я не помню, использовал ли я это на самом деле. Если бы я вспомнил, что он был там, я бы, вероятно, указал на него, вместо того, чтобы быстро перемешать этот образец. - person RBerteig; 21.07.2009
comment
Без проблем. Наличие нескольких примеров использования Lua C API, вероятно, будет более полезным для OP. OTOH, мой ae действительно демонстрирует некоторые методы, которые можно было бы назвать продвинутыми, например, использование функций чтения, чтобы избежать динамического построения строки. Он также открывает математическую библиотеку в глобальной области видимости, чего и следовало ожидать от оценщика выражений, то есть sin, а не math.sin. - person lhf; 22.07.2009
comment
Я отредактировал указатель на ae и рекомендацию посмотреть на него для более продвинутого использования Lua API. Чем больше я использую Lua для подобных вещей, тем больше меня впечатляет легкость, с которой они могут быть выполнены. - person RBerteig; 22.07.2009

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

person 0x6adb015    schedule 21.07.2009
comment
Я просмотрел документацию, и она очень легко справится с задачей. просто нужно убедиться, что ввод легко изменить на синтаксис lua, и вперед. Это так круто: D +1 - person hhafez; 21.07.2009
comment
Я знал, что есть более простой способ сделать это, чем те, которые цитируются здесь stackoverflow.com/questions/1151127 - person hhafez; 21.07.2009

Это для пользователей 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.

person user1771398    schedule 06.02.2015

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

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

person Greg Hewgill    schedule 20.07.2009
comment
Конечно, но из-за лени я хочу выбрать, так сказать, путь наименьшего сопротивления. Отсюда у меня много вопросов: p - person hhafez; 21.07.2009
comment
Я был приятно удивлен простотой взаимодействия между C и Lua. Следуйте примерам, и вы тоже убедитесь, насколько безошибочен весь процесс! - person 0x6adb015; 21.07.2009

person    schedule
comment
Вы должны объяснить, как ваш код решает вопрос OP. - person gp_sflover; 22.04.2017