当前位置 : 主页 > 网络编程 > lua >

使用Lua评估数学表达式

来源:互联网 收集:自由互联 发布时间:2021-06-23
在我的 previous question中,我一直在寻找一种在C中使用复杂数学表达式的方法,大多数建议都需要实现某种类型的解析器. 但是one answer,建议使用Lua来评估表达式.我对这种方法感兴趣,但我对
在我的 previous question中,我一直在寻找一种在C中使用复杂数学表达式的方法,大多数建议都需要实现某种类型的解析器.

但是one answer,建议使用Lua来评估表达式.我对这种方法感兴趣,但我对Lua一无所知.

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

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

谢谢

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

y = x ^ 2 1 / x – cos(x)

评估y的x值范围

设置一个Lua解释器实例,然后传递它要表达的表达式,返回一个调用表达式的函数来直接进行评估.你甚至可以让用户拥有变量……

这是我编写的示例代码,并编辑到我的其他答案中.在任何情况下,它最好放在标记为Lua的问题上,所以我也在这里添加它.我编译了这个并尝试了几个案例,但在生产代码中肯定不应该信任它而不需要注意错误处理等等.所有常见的警告都适用于此处.

我在Windows上使用Lua 5.1.4从Lua for Windows编译并测试了这个.在其他平台上,你必须从你的常用来源或www.lua.org找到Lua.

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

我鼓励读者在lhf之前查看更多可用于生产的ae库,以获取利用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以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 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行代码,包括一系列注释,空行和演示.对于知道如何评估一个变量的合理任意表达式的快速函数求值程序来说并不错,并且在其beck和call上有rich library of standard math functions.

您可以在其下面使用图灵完备语言,这将是一个简单的扩展,允许用户定义完整的函数以及评估简单表达式.

网友评论