在我的previous question中,我一直在寻找一种用C语言表达复杂数学表达式的方法,大多数建议都需要实现某种类型的解析器。
然而one answer,建议使用Lua来评估表达式。我对这种方法感兴趣,但我对Lua一无所知。
有一个有Lua经验的人可以解释一下吗?
具体我想知道的是 Lua提供哪种API可以评估以字符串形式传入的数学表达式?如果没有API可以做这样的事情,可能有人会对链接的答案有所了解,因为它似乎是一个很好的方法:)
由于
我想要评估的表达式类型有一些用户输入,例如
y = x ^ 2 + 1 / x - cos(x)
评估y的x值范围
答案 0 :(得分:5)
设置Lua解释器实例并将其传递给要计算的表达式是直接的,返回一个调用该函数来计算表达式的函数。你甚至可以让用户拥有变量......
这是我编写的示例代码并编辑到我的其他答案中。在任何情况下,它最好放在标记为Lua的问题上,所以我也在这里添加它。我编译了这个并尝试了几个案例,但在生产代码中肯定不应该信任它而不需要注意错误处理等等。所有常见的警告都适用于此。
我使用来自Lua for Windows的Lua 5.1.4在Windows上编译和测试了这个。在其他平台上,您必须从常用来源或www.lua.org找到Lua。
更新:此示例使用简单直接的技术,尽可能简单地隐藏Lua API的全部功能和复杂性。它原样可能有用,但可以通过多种方式进行改进。
我鼓励读者通过ae查看更多生产就绪的lhf库,以获取利用API的代码,以避免我使用过的快速和脏字符串操作。他的图书馆还将数学库推广到全球名称空间,以便用户可以说sin(x)
或2 * pi
而无需说math.sin
等等。
这是文件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);
这是文件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.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。
在它下面有一个图灵完备的语言,这将是一个简单的扩展,允许用户定义完整的函数以及评估简单的表达式。
答案 1 :(得分:4)
由于你像大多数程序员一样懒惰,这里有一个简单示例的链接,您可以使用arbitrary code解析一些Lua。从那里开始,创建表达式解析器应该很简单。
答案 2 :(得分:2)
这适用于正在寻找相当于&#34; eval&#34;的Lua用户。
这个神奇的词曾经是loadtring,但现在,自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中进行数学运算。
答案 3 :(得分:1)
Lua文档包含一个标题为The Application Programming Interface的部分,其中介绍了如何从C程序中调用Lua。 Lua的文档非常好,你甚至可以在那里找到你想要做的例子。
这是一个很大的世界,所以无论你选择自己的解析解决方案还是像Lua这样的嵌入式解释器,你都会有一些工作要做!
答案 4 :(得分:0)
function calc(operation)
return load("return " .. operation)()
end