문제

이전 질문 나는 C에서 복잡한 수학적 표현을 평가하는 방법을 찾고 있었는데 대부분의 제안에는 특정 유형의 파서 구현이 필요했습니다.

하지만 답은 하나, 표현식을 평가하기 위해 Lua를 사용하는 것이 좋습니다.나는 이 접근 방식에 관심이 있지만 Lua에 대해서는 아무것도 모릅니다.

Lua 경험이 있는 사람이 좀 알려줄 수 있나요?

구체적으로 제가 알고 싶은 것은문자열로 전달된 수학적 표현식을 평가할 수 있도록 Lua에서 제공하는 API는 무엇입니까? 그러한 작업을 수행하는 API가 없다면 좋은 접근 방식처럼 연결된 답변에 대해 밝힐 수 있는 사람이 있을 수 있습니다. :)

감사해요

평가하려는 표현식 유형에는 다음과 같은 사용자 입력이 제공됩니다.

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

x 값의 범위에 대해 y를 평가합니다.

도움이 되었습니까?

해결책

Lua 인터프리터 인스턴스를 설정하고 평가할 표현식을 전달하고 표현식을 평가하는 호출 함수를 다시 가져오는 것은 간단합니다.사용자에게 변수를 부여할 수도 있습니다...

내가 요리하고 다른 답변으로 편집한 샘플 코드는 다음과 같습니다.어쨌든 Lua라는 태그가 붙은 질문에 배치하는 것이 더 나을 것이므로 여기에도 추가하겠습니다.나는 이것을 컴파일하고 몇 가지 사례에 대해 시도했지만 오류 처리 등에 대한 주의 없이 프로덕션 코드에서 신뢰해서는 안 됩니다.여기에는 일반적인 주의 사항이 모두 적용됩니다.

나는 Lua 5.1.4를 사용하여 Windows에서 이것을 컴파일하고 테스트했습니다. 윈도우용 루아.다른 플랫폼에서는 일반적인 소스나 www.lua.org에서 Lua를 찾아야 합니다.

업데이트: 이 샘플은 간단하고 직접적인 기술을 사용하여 가능한 한 단순한 인터페이스 뒤에 Lua API의 모든 기능과 복잡성을 숨깁니다.현재로서는 유용할 수 있지만 여러 가지 방법으로 개선될 수 있습니다.

나는 독자들이 훨씬 더 생산 준비가 된 것을 살펴 보도록 권장합니다. 도서관 lhf 제가 사용한 빠르고 지저분한 문자열 조작을 피하기 위해 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입니다.단일 명령줄 인수를 가져와 표현식으로 로드한 다음 11단계에 걸쳐 전역 변수 x를 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줄의 코드로 구성됩니다.하나의 변수에 대한 임의의 표현식을 합리적으로 평가하는 방법을 알고 있고 풍부한 표준 수학 함수 라이브러리 손짓으로 부르면 됩니다.

그 밑에는 Turing-complete 언어가 있으며, 사용자가 완전한 기능을 정의하고 간단한 표현식을 평가할 수 있도록 하는 쉬운 확장이 될 것입니다.

다른 팁

대부분의 프로그래머와 마찬가지로 게으르기 때문에 여기에 일부를 구문 분석하는 데 사용할 수있는 간단한 예에 대한 링크가 있습니다. 임의 코드 사용 루아. 거기에서 표현 파서를 만드는 것은 간단해야합니다.

이것은 "Eval"과 동등한 LUA를 찾고있는 LUA 사용자를위한 것입니다.

마법의 단어는로드 스트링 이었지만 지금은 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는 평가할 문자열입니다.

load(s)()

항상 그렇듯이 평가 메커니즘은 비싸고 읽기 어려운 코드를 생성하기 때문에 가능한 경우 피해야합니다. 나는 개인적 으로이 메커니즘을 Luatex/Lualatex와 함께 사용하여 라텍스에서 수학 작업을합니다.

LUA 문서에는 섹션이 포함되어 있습니다 응용 프로그램 프로그래밍 인터페이스 C 프로그램에서 LUA를 호출하는 방법을 설명합니다. LUA에 대한 문서는 매우 좋으며 거기에서하고 싶은 일의 예를 찾을 수도 있습니다.

그것은 그곳에있는 큰 세상이므로 자신의 구문 분석 솔루션을 선택하든 LUA와 같은 임베드 가능한 통역사를 선택하든 할 일이 있습니다!

function calc(operation)
    return load("return " .. operation)()
end
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top