Lua を使用した数式の評価
-
18-09-2019 - |
質問
私の中で 前の質問 私は C で複雑な数式を評価する方法を探していましたが、ほとんどの提案では何らかのタイプのパーサーを実装する必要がありました。
しかし 一つの答え, 、式の評価に Lua を使用することを提案しました。このアプローチには興味がありますが、Lua については何も知りません。
Lua の経験がある人に何か光を当ててもらえますか?
具体的に知りたいのは文字列として渡された数式を評価できる API が Lua に提供されている場合はどれですか? そのようなことを行うための API がない場合は、リンクされた回答に光を当てることができるかもしれません。それは良いアプローチのように見えます:)
ありがとう
評価したい式のタイプには、次のようなユーザー入力が与えられます。
y = x^2 + 1/x - cos(x)
x の値の範囲に対して y を評価します。
解決
Lua インタープリターのインスタンスをセットアップし、評価する式をそれに渡し、その式を評価する呼び出し関数を取得するのは簡単です。ユーザーに変数を持たせることもできます...
これは、私が作成して他の回答に編集したサンプルコードです。いずれにせよ、Lua というタグが付いた質問に置く方がよいでしょう。そのため、ここにも追加します。これをコンパイルしていくつかのケースで試してみましたが、エラー処理などにある程度の注意を払わない限り、実稼働コードでは信頼すべきではありません。ここでは通常の注意事項がすべて適用されます。
これを Windows で Lua 5.1.4 を使用してコンパイルし、テストしました。 Windows 用 Lua. 。他のプラットフォームでは、通常のソースまたは www.lua.org から Lua を見つける必要があります。
アップデート: このサンプルでは、シンプルかつ直接的な手法を使用して、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の実装
ここは 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 行のコードで構成されています。1 つの変数の合理的に任意の式を評価する方法を知っている迅速な関数評価器としては悪くありません。 標準数学関数の豊富なライブラリ 思いのままに。
その根底にはチューリング完全言語があり、これを簡単に拡張すると、ユーザーは完全な関数を定義したり、単純な式を評価したりできるようになります。
他のヒント
は、ほとんどのプログラマのように、ここであなたには、いくつかの<のhref =「http://www.lua.org/pil/24.1.html」のrel =を解析するために使用できる簡単な例へのリンクがあります"nofollowをnoreferrer"> のLUA を用いの任意のコード。そこから、それはあなたの表現パーサーを作成するのは簡単である必要があります。
このは「evalの」のLuaの同等を探しているLuaのユーザーを対象としてます。
loadStringはするために使用されるマジックワードが、それは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
間に合わせとウェイが何をしたようは、あなたはsが評価する文字列があるのeval(s)は、以下の同等のものを考えることができます:
load(s)()
彼らは高価であり、読みにくいコードを生成するので、可能な場合、は、いつものように、evalのメカニズムは避けるべきです。 私は個人的にはラテックスで数学演算を行うためにLuaTex / LuaLatexでこのメカニズムを使用します。
Luaのドキュメントはアプリケーション・プログラミング・インタフェースのというセクションが含まれていますこれはあなたのCプログラムからのLuaを呼び出す方法について説明します。 Luaのためのドキュメントは非常に良好であり、あなたも、あなたはそこに何をしたいのかの例を見つけることができる可能性があります。
それはあなたがあなた自身の構文解析ソリューションやLuaのような、埋め込みインタプリタを選択するかどうかので、あなたが行うにはいくつかの仕事をするんだ、そこに大きな世界だ!
function calc(operation)
return load("return " .. operation)()
end