【问题标题】:Evaluating Mathematical Expressions using Lua使用 Lua 评估数学表达式
【发布时间】:2010-11-12 11:55:25
【问题描述】:

在我的previous question 中,我正在寻找一种在 C 中计算复杂数学表达式的方法,大多数建议都需要实现某种类型的解析器。

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

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

我特别想知道的是 Lua 提供了哪些 API(如果有)可以评估作为字符串传入的数学表达式?似乎是一个好方法:)

谢谢

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

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

为一系列 x 值计算 y

【问题讨论】:

  • 你想知道什么?语法?不管有没有可能?表现?请详细说明。
  • 以粗体查看我的编辑 ;) 感谢您的评论
  • 你为什么不干脆(但简单)的方式?
  • 什么是漫长而简单的方法?

标签: lua mathematical-expressions


【解决方案1】:

Lua 文档包含一个标题为The Application Programming Interface 的部分,它描述了如何从 C 程序调用 Lua。 Lua 的文档非常好,您甚至可以在其中找到您想要做什么的示例。

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

【讨论】:

  • 当然,但是懒惰的我想选择阻力最小的路径。因此我有很多问题:p
  • C 和 Lua 之间的轻松交互让我感到惊喜。按照示例进行操作,您也会看到整个过程是多么无错误!
【解决方案2】:

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

【讨论】:

  • 我查看了文档,这很容易完成这项工作。只需确保输入很容易更改为 lua 语法,然后我们就可以走了。太酷了:D +1
  • 我知道有一种比这里引用的方法更简单的方法stackoverflow.com/questions/1151127
【解决方案3】:

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

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

我使用来自Lua for Windows 的 Lua 5.1.4 在 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 表达式评估器:

#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 行代码,包括一些 cmet、空白行和演示。对于知道如何计算一个变量的合理任意表达式并且有rich library of standard math functions 随叫随到的快速函数评估器来说,这还不错。

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

【讨论】:

  • @lhf,当然,这并不奇怪......我过去曾在你的有用绑定和实用程序列表中看到过,但我不记得我是否真的使用过它。如果我记得它在那里,我可能会指向它,而不是快速将这个样本扔在一起。
  • 没问题。有几个如何使用 Lua C API 的示例可能对 OP 更有帮助。 OTOH,我的 ae 确实展示了一些可以称为高级的技术,例如使用读取器函数来避免动态构建字符串。它还在全局范围内打开数学库,这是您在表达式求值器中所期望的,即 sin,而不是 math.sin。
  • 我编辑了一个指向 ae 的指针,并建议查看它以更高级地使用 Lua API。我用 Lua 做这类事情的次数越多,我就越能轻松地完成这些事情。
【解决方案4】:

这适用于正在寻找与“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 中进行数学运算。

【讨论】:

    【解决方案5】:
    function calc(operation)
        return load("return " .. operation)()
    end
    

    【讨论】:

    • 您应该解释您的代码如何解决 OP 问题。
    猜你喜欢
    • 2010-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-16
    • 2011-06-30
    • 1970-01-01
    相关资源
    最近更新 更多