Pergunta

Na minha anterior eu estava procurando uma maneira de evaulating expressões matemáticas complexas em C, a maioria das sugestões necessário implementar algum tipo de analisador.

No entanto resposta de uma , sugeriu o uso de Lua para avaliar a expressão. Estou interessado nesta abordagem, mas eu não sei nada sobre Lua.

Pode alguém com experiência na Lua lançar alguma luz?

Especificamente o que eu gostaria de saber é Qual API se algum faz Lua prever que pode avaliar expressões matemáticas passados ??como uma string? Se não houver nenhuma API para fazer tal coisa, pode ser que alguém pode lançar alguma luz sobre a resposta ligada como parecia uma boa abordagem:)

Graças

O tipo de expressão que eu gostaria de avaliar é dada alguma entrada do usuário, como

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

avaliar y para uma gama de valores de x

Foi útil?

Solução

É simples de configurar uma instância do interpretador Lua, e passá-lo expressões a serem avaliadas, ficando para trás uma função a ser chamada que avalia a expressão. Você pode até mesmo permitir que o usuário tem variáveis ??...

Aqui está o código de exemplo que cozinhou e editado na minha outra resposta. É provavelmente melhor colocado sobre uma questão marcado Lua em qualquer caso, por isso estou adicionando-o aqui também. Eu compilei esta e tentou fazê-lo durante alguns casos, mas certamente não deve ser confiável no código de produção sem alguma atenção para o tratamento de erros e assim por diante. Todas as advertências se aplicam aqui.

Eu compilei e testou este no Windows usando Lua 5.1.4 de Lua para Windows . Em outras plataformas, você terá que encontrar Lua a partir de sua fonte de costume, ou de www.lua.org.

Atualização : Esta simples usos de amostra e técnicas diretas para esconder o poder e complexidade da API Lua trás o mais simples possível uma interface. É provavelmente útil como-é, mas poderia ser melhorado em vários aspectos.

Gostaria de incentivar os leitores a olhar para o muito mais pronto para produção ae biblioteca por LHF para o código que aproveita a API para evitar algumas das rápida e suja a manipulação de cadeia que eu usei. Sua biblioteca também promove a biblioteca de matemática para o espaço de nome global para que o usuário pode dizer sin(x) ou 2 * pi sem ter que dizer math.sin e assim por diante.

Interface pública para LE

Aqui está a le.h arquivo:

/* 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);

Exemplo de código usando LE

Aqui está o arquivo t-le.c, demonstrando um uso simples desta biblioteca. Ela leva o seu argumento de linha de comando único, carrega-lo como uma expressão, e avalia com a variável global x mudança de 0.0 a 1.0 em 11 passos:

#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);
    }
}

Aqui está alguma saída de 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:...>

Implementação de LE

Aqui está le.c, implementando o Expression avaliador 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;
}

Observações

A amostra anterior consiste de 189 linhas de código total, incluindo uma salpicos de observações, as linhas em branco, e a demonstração. Nada mal para um avaliador de função rápida que sabe como avaliar expressões razoavelmente arbitrários de uma variável, e tem rica biblioteca de funções matemáticas padrão no seu beck e chamada.

Você tem uma linguagem Turing-completo por baixo de tudo, e seria uma extensão fácil para permitir ao usuário definir funções completas, bem como para avaliar expressões simples.

Outras dicas

Uma vez que você é preguiçoso, como a maioria dos programadores, aqui está um link para um exemplo simples que você pode usar para analisar alguns código arbitrário usando Lua . De lá, ele deve ser simples para criar o seu analisador de expressão.

Esta é para os usuários Lua que estão à procura de um equivalente Lua de "eval".

A palavra mágica costumava ser loadstring mas é agora, já que Lua 5.2, uma versão atualizada do load .

 i=0
 f = load("i = i + 1") -- f is a function
 f() ; print(i) -- will produce 1
 f() ; print(i) -- will produce 2

Outro exemplo, que oferece um valor:

f=load('return 2+3')
print(f()) -- print 5

Como uma maneira rápida e suja de fazer, você pode considerar as seguintes equivalente a eval (s), onde s é uma string para avaliar:

load(s)()

Como sempre, mecanismos eval deve ser evitado quando possível, pois eles são caros e produzir um código difícil de ler. Eu pessoalmente uso este mecanismo com luatex / LuaLatex para fazer operações matemáticas em Latex.

A documentação Lua contém uma seção intitulada O Application Programming Interface que descreve como chamar Lua de seu programa C. A documentação para Lua é muito bom e você pode até ser capaz de encontrar um exemplo do que você quer fazer lá dentro.

É um grande mundo lá dentro, então se você escolher sua própria solução de análise ou um intérprete embutido como Lua, você vai ter algum trabalho a fazer!

function calc(operation)
    return load("return " .. operation)()
end
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top