5

以前の質問では、C で複雑な数式を評価する方法を探していましたが、ほとんどの提案では、何らかのタイプのパーサーを実装する必要がありました。

ただし、式を評価するためにLuaを使用することを提案した1つの答え。私はこのアプローチに興味がありますが、Lua については何も知りません。

Lua の経験のある人は、光を当てることができますか?

特に知りたいのは 、文字列として渡された数式を評価できる Lua が提供する API がある場合、どの 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 のすべての機能と複雑さを可能な限りシンプルなインターフェイスの背後に隠しています。現状のままでもおそらく有用ですが、さまざまな点で改善される可能性があります。

API を利用して、私が使用した迅速で汚い文字列操作の一部を回避するコードについては、 lhfによるはるかに本番対応のaeライブラリを調べることをお勧めします。彼のライブラリは、数学ライブラリをグローバルな名前空間に昇格させて、ユーザーが言うことも、言わなくても言うこともできるようにします。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 で、このライブラリの簡単な使い方を示しています。単一のコマンドライン引数を受け取り、それを式としてロードし、11 段階で 0.0 から 1.0 に変化するグローバル変数 x で評価します。

#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

え:…>

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 行のコードで構成されています。1 つの変数の合理的に任意の式を評価する方法を知っており、標準的な数学関数の豊富なライブラリを必要に応じて利用できる迅速な関数評価器としては悪くありません。

そのすべての下にチューリング完全言語があり、ユーザーが完全な関数を定義したり、単純な式を評価したりできるようにするための簡単な拡張になります。

于 2009-07-21T07:28:03.603 に答える
4

ほとんどのプログラマーのように怠惰なので、 Luaを使用して任意のコードを解析するために使用できる簡単な例へのリンクを次に示します。そこから、式パーサーを簡単に作成できるはずです。

于 2009-07-21T00:04:40.127 に答える
1

Lua のドキュメントには、C プログラムから Lua を呼び出す方法を説明するThe Application Programming Interfaceというタイトルのセクションが含まれています。Lua のドキュメントは非常に優れており、そこでやりたいことの例を見つけることさえできるかもしれません。

そこには大きな世界があるので、独自の解析ソリューションを選択するか、Lua のような組み込み可能なインタープリターを選択するかにかかわらず、やるべきことがいくつかあります!

于 2009-07-20T23:49:13.807 に答える