Вычисление математических выражений с использованием Lua

StackOverflow https://stackoverflow.com/questions/1156572

  •  18-09-2019
  •  | 
  •  

Вопрос

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

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

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

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

Спасибо

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

у = х^2 + 1/х - потому что (х)

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

Это было полезно?

Решение

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

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

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

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

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

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

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

Другие советы

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

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

Раньше волшебным словом была строка загрузки, но теперь, начиная с Lua 5.2, это обновленная версия нагрузка.

 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)()

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

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

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

function calc(operation)
    return load("return " .. operation)()
end
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top