5

在我之前的问题中,我正在寻找一种在 C 中评估复杂数学表达式的方法,大多数建议都需要实现某种类型的解析器。

然而,一个答案建议使用 Lua 来评估表达式。我对这种方法很感兴趣,但我对 Lua 一无所知。

有Lua经验的人可以解释一下吗?

具体来说,我想知道的是 Lua 提供了哪个 API 可以评估作为字符串传入的数学表达式?如果没有 API 来做这样的事情,可能有人可以对链接的答案有所了解,因为这似乎是一个好方法:)

谢谢

我想评估的表达式类型给出了一些用户输入,例如

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

为 x 的一系列值评估 y

4

5 回答 5

6

设置一个 Lua 解释器实例,并将表达式传递给它以进行评估,返回一个函数来调用来评估表达式,这很简单。你甚至可以让用户有变量......

这是我编写并编辑到我的其他答案中的示例代码。无论如何,它可能更好地放在一个标记为 Lua 的问题上,所以我也在这里添加它。我编译了这个并尝试了几个案例,但如果不注意错误处理等,它肯定不应该在生产代码中被信任。所有常见的警告都适用于此。

我使用Lua for Windows 的Lua 5.1.4 在 Windows 上编译和测试了它。在其他平台上,您必须从通常的来源或 www.lua.org 找到 Lua。

更新:此示例使用简单直接的技术将 Lua API 的全部功能和复杂性隐藏在尽可能简单的接口后面。它可能按原样有用,但可以通过多种方式进行改进。

我会鼓励读者研究lhf提供的更适合生产的ae库,以获取利用 API 来避免我使用的一些快速和肮脏的字符串操作的代码。他的库还将数学库提升到全局名称空间中,以便用户可以说或不必说等等。sin(x)2 * pimath.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 在 11 个步骤中从 0.0 变为 1.0 对其进行评估:

#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 表达式评估器:

#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 行代码,包括大量注释、空白行和演示。对于知道如何合理地评估一个变量的任意表达式并且拥有丰富的标准数学函数库的快速函数评估器来说还不错。

你有一个图灵完备的语言,它是一个简单的扩展,允许用户定义完整的函数以及评估简单的表达式。

于 2009-07-21T07:28:03.603 回答
4

由于您像大多数程序员一样懒惰,因此这里有一个简单示例的链接,您可以使用该示例使用Lua解析一些任意代码。从那里开始,创建表达式解析器应该很简单。

于 2009-07-21T00:04:40.127 回答
3

这适用于正在寻找相当于“eval”的 Lua 的 Lua 用户。

神奇的词曾经是 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 中进行数学运算。

于 2015-02-06T18:02:54.733 回答
1

Lua 文档包含一个名为“应用程序编程接口”的部分,它描述了如何从 C 程序中调用 Lua。Lua 的文档非常好,您甚至可以在其中找到您想要执行的操作的示例。

这是一个很大的世界,所以无论您选择自己的解析解决方案还是像 Lua 这样的嵌入式解释器,您都需要做一些工作!

于 2009-07-20T23:49:13.807 回答
1
function calc(operation)
    return load("return " .. operation)()
end
于 2017-04-22T14:35:44.327 回答