大家看看有什么解法。

goldbeef 2011-12-06 08:36:47
最近和同学在做一个数值计算软件,在非线性方程的数值解法模块中,我想往函数中 以字符串形式传入一个字符串方程表达式(姑且认为是一元的,比如“sin(x)+5*x*x+”),我想问下有什么好的方法,对字符串方程进行识别,并在函数里面进行变量(比如x)替换以及求值.记得之前我们学编译原理的时候是自己设计的编译器进行一些类似操作,但感觉任务量比较大。所以想问下大家有什么好的方法,还是已经有什么现成的库或者源码可以用。
...全文
175 7 打赏 收藏 转发到动态 举报
写回复
用AI写文章
7 条回复
切换为时间正序
请发表友善的回复…
发表回复
icemornings 2011-12-06
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 icemornings 的回复:]

以前写的一个表达式求值的代码,没有加入变量的功能,但是加上应该不难……应该……
C/C++ code

/*
* Name: main.c
* Purpose: 算术表达式求值
* Author: icemornings
* Created: Fri Nov 25 21:45:37 2011
* Copyright: (C) icemornings……
[/Quote]
刚随手改了下代码,加上变量求值的功能……当然类似于sin(),cos()之类的没有实现……加上应该不难……不难……
以下代码,其中calc_func.h和calc_func.c中是5楼中几个函数的声明以及定义。

/*
* Name: main.c
* Purpose: 主程序
* Author: icemornings
* Created: Tue Dec 06 20:56:34 2011
* Copyright: (C) icemornings All rights reserved.
* Licence: GNU GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "calc_func.h" /* 之前写的几个函数丢这里面了 */

/* 以下许多东西都是随手定义,实际使用时请酌情加上参数检查,边界检查 */

struct VAR_VAL
{
char var;
char val[9]; /* 随便定义个8位数 */
};

/* 取得所有变量值,形成新的无未知变量的表达式 */
int get_var_val(const char equ[], char infix[]);

int main(int argc, char **argv)
{
enum {BUF_SIZE = 128};
char equation[BUF_SIZE] = {0};
char infix_exp[BUF_SIZE] = {0};
char rpn_exp[BUF_SIZE] = {0};

int result = 0;

/* 取得方程式 */
printf("Input Equation: ");
fgets(equation, BUF_SIZE, stdin);

/* 解析方程式 */
get_var_val(equation, infix_exp);

/* 转成逆波兰表达式 */
infix2rpn(infix_exp, rpn_exp);
printf("RPN: %s\n", rpn_exp);

/* 计算逆波兰表达式 */
calc_rpn(rpn_exp, &result);

printf("RESULT: %d\n", result);
return 0;
}

int get_var_val(const char equ[], char infix[])
{
struct VAR_VAL var_val[8] = {0};
int var_num = 0;

int i;

while ('\0' != *equ)
{
/* 字母判定为变量 */
if (isalpha(*equ))
{
/* 这个变量是否已经取得其值 */
for (i = 0; i < var_num; ++i)
{
if (*equ == var_val[i].var)
break;
}

/* 未取得,新变量,取之 */
if (i >= var_num)
{
printf("%c = ", *equ);
scanf("%s", var_val[i].val);

++var_num;
}

/* 替换之 */
memcpy(infix, var_val[i].val, strlen(var_val[i].val));

infix += strlen(var_val[i].val);
++equ;
continue;
}

*(infix++) = *(equ++);
}

return 0;
}
/* End Of File */



测试了一下:

Input Equation: x^2 + y ^z + 10
x = 10
y = 2
z = 3
RPN: 10,2,^,2,3,^,+,10,+,
RESULT: 118
请按任意键继续. . .
柯本 2011-12-06
  • 打赏
  • 举报
回复
找到上面程序出错原因, CoUninitialize();去掉就不错了
另外,找了个更简的方法,来调用MicroSoft Scrpit Control
供参考:

#include <windows.h>
#include <stdio.h>
#import "C:\windows\system32\msscript.ocx" raw_interfaces_only, raw_native_types, no_namespace, named_guids

int main()
{
_variant_t vReturn;
double returnValue;
::CoInitialize(NULL);

::IScriptControlPtr p(__uuidof(ScriptControl));
p.AddRef();
p->put_Language(_bstr_t("JScript"));
p->put_AllowUI(TRUE);
p->AddCode(_bstr_t("x=1.2;"));
p->AddCode(_bstr_t("function test() { return Math.sin(x)+x*x; }"));


printf("ok\n");

p->Eval(_bstr_t("test()"), &vReturn);
returnValue = vReturn.dblVal;
p->Release();

printf("%lf\n", returnValue);
system("pause");
return 0;
}



icemornings 2011-12-06
  • 打赏
  • 举报
回复
以前写的一个表达式求值的代码,没有加入变量的功能,但是加上应该不难……应该……

/*
* Name: main.c
* Purpose: 算术表达式求值
* Author: icemornings
* Created: Fri Nov 25 21:45:37 2011
* Copyright: (C) icemornings All rights reserved.
* Licence: GNU GPL
*/
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>

/* 为了简化代码,忽视边界,尽量定义大一点的缓冲区吧 */

/* 模拟栈的操作 */
#define PUSH(stack_name, val) stack_name[++(stack_name##_sp)] = val
#define POP(stack_name) stack_name[(stack_name##_sp)--]
#define TOP(stack_name) stack_name[stack_name##_sp]
#define EMPTY(stack_name) ((stack_name##_sp) < 0)

/* 取得运算符优先级 */
int opt_rank(char opt);
/* 中缀表达式转成逆波兰表达式 */
int infix2rpn(const char *infix, char *rpn);
/* 计算逆波兰表达式 */
int calc_rpn(const char *rpn, int *result);

int main(int argc, char **argv)
{
char infix_exp[128] = {0};
char rpn_exp[128] = {0};

int result = 0;

/* 取得中缀表达式 */
fgets(infix_exp, 128, stdin);

/* 转换成逆波兰表达式 */
if (infix2rpn(infix_exp, rpn_exp) < 0)
{
printf("Can't Conv Infix to RPN!\n");
}
else
{
printf("INFIX: %s\n", infix_exp);
printf("RPN: %s\n", rpn_exp);
/* 计算逆波兰表达式 */
if (calc_rpn(rpn_exp, &result) < 0)
printf("Error RPN!\n");
else
printf("RESULT: %d\n", result);
}
return 0;
}

int opt_rank(char opt)
{
switch (opt)
{
case '\n':
case '#':
return 0;
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
default:
break;
}
return -1;
}

int infix2rpn(const char *infix, char *rpn)
{
/* 运算符栈以及栈指针 */
char opt_stack[128];
int opt_stack_sp = -1;

int last_is_num = 0;
int cur_opt_rank = -1;

while ('\0' != *infix)
{
/* 如果是数字,读取所有数字 */
if (isdigit(*infix))
{
while (isdigit(*infix))
{
*(rpn++) = *(infix++);
}
*(rpn++) = ',';
/* 标记前一个是数字 */
last_is_num = 1;
}
else
{
/* 如果是'('直接入栈 */
if ('(' == *infix)
{
PUSH(opt_stack, '(');
}
else if (')' == *infix)
{
/* 如果是')'则出栈,直到遇到'(' */
while (!EMPTY(opt_stack) && '(' != TOP(opt_stack))
{
*(rpn++) = POP(opt_stack);
*(rpn++) = ',';
}

/* 如果此时栈空这说明发生了括号不匹配 */
if (EMPTY(opt_stack))
return -1;
POP(opt_stack);
}
else
{
/* 取得运算符优先级 */
cur_opt_rank = opt_rank(*infix);
/* >= 0表示是合法的运算符 */
if (cur_opt_rank >= 0)
{
/* 如果前面处理的不是数字,贸然出现的运算符肯定有问题 */
if (!last_is_num)
return -1;

/* 弹出所有优先级比当前运算符高的运算符 */
while (!EMPTY(opt_stack) && opt_rank(TOP(opt_stack)) >= cur_opt_rank)
{
*(rpn++) = POP(opt_stack);
*(rpn++) = ',';
}
/* 当前运算符入栈 */
PUSH(opt_stack, *infix);
last_is_num = 0;
}
}
++infix;
}
}

return 0;
}

int calc_rpn(const char *rpn, int *result)
{
/* 结果栈以及指针 */
int num_stack[128];
int num_stack_sp = -1;

char num[16] = {0};
int i = 0;
int param1, param2;

while ('\0' != *rpn)
{
/* 如果是数字,读取整个数字,转成实际数值并入栈 */
if (isdigit(*rpn))
{
i = 0;
while (isdigit(*rpn))
{
num[i++] = *(rpn++);
}
num[i] = '\0';
PUSH(num_stack, atoi(num));
}
else if ('+' == *rpn || '-' == *rpn || '*' == *rpn || '/' == *rpn || '^' == *rpn)
{
/* 是运算符,则从栈中弹出运算符需要的操作数 */
if (EMPTY(num_stack))
return -1;
param1 = POP(num_stack);

if (EMPTY(num_stack))
return -1;
param2 = POP(num_stack);

/* 计算,并把结果入栈 */
switch (*rpn)
{
case '+':
PUSH(num_stack, param1 + param2);
break;
case '-':
PUSH(num_stack, param2 - param1);
break;
case '*':
PUSH(num_stack, param1 * param2);
break;
case '/':
PUSH(num_stack, param2 / param1);
break;
case '^':
PUSH(num_stack,(int)pow(param2, param1));
break;
default:
break;
}
}

++rpn;
}

if (EMPTY(num_stack))
return -1;

/* 栈顶就是所有计算所得,不知道会不会有多个结果,懒得理会了 */
*result = TOP(num_stack);
return 0;

}
/* End Of File */

柯本 2011-12-06
  • 打赏
  • 举报
回复
以下程序是我从MSDN上改写的,仅供参考
它原来程序运行结果后就会报错(且MSDN程序编译都有问题)
它原有的三个参数,我用了其中一个
环境
WINSP+SP3
VS2005及VC6都可以编译通过,得到结果
所以,这个只能说提供个思路(我以前类似问题都是用CB解决的)
具体的自己研究下

#include <stdio.h>
#import "C:\windows\system32\msscript.ocx" // msscript.ocx
using namespace MSScriptControl;

int main(void)
{
HRESULT hr = CoInitialize(NULL);

IScriptControlPtr pScriptControl(__uuidof(ScriptControl));

// Create a VARIANT array of VARIANTs which hold BSTRs
LPSAFEARRAY psa;
SAFEARRAYBOUND rgsabound[] = { 3, 0 }; // 3 elements, 0-based
int i;

psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
if (!psa)
{
return E_OUTOFMEMORY;
}

VARIANT vFlavors[3];
for (i = 0; i < 3; i++)
{
VariantInit(&vFlavors[i]);
V_VT(&vFlavors[i]) = VT_BSTR;
}

V_BSTR(&vFlavors[0]) = SysAllocString(OLESTR("1.2"));
V_BSTR(&vFlavors[1]) = SysAllocString(OLESTR("Chocolate"));
V_BSTR(&vFlavors[2]) = SysAllocString(OLESTR("Espresso Chip"));

long lZero = 0;
long lOne = 1;
long lTwo = 2;

// Put Elements to the SafeArray:
hr = SafeArrayPutElement(psa, &lZero,&vFlavors[0]);
hr = SafeArrayPutElement(psa, &lOne,&vFlavors[1]);
hr = SafeArrayPutElement(psa, <wo,&vFlavors[2]);

// Free Elements from the SafeArray:
for(i=0;i<3;i++)
{
SysFreeString(vFlavors[i].bstrVal);
}

// Set up Script control properties
pScriptControl->Language = "JScript";
pScriptControl->AllowUI = TRUE;
pScriptControl->AddCode("function MyStringFunction(x,Argu2,Argu3) { return Math.sin(x)+x*x;}" );

// Call MyStringFunction with the two args:
_variant_t outpar = pScriptControl->Run("MyStringFunction", &psa);


// Convert VARIANT to C string:
_bstr_t bstrReturn = (_bstr_t)outpar;
char *pResult = (char *)bstrReturn;


// Print the result out:
printf("func=%s\n",pResult);

// Clean up:
SafeArrayDestroy(psa);

CoUninitialize();
return(0);
}


编程点滴 2011-12-06
  • 打赏
  • 举报
回复
感觉好难啊
柯本 2011-12-06
  • 打赏
  • 举报
回复
这个,如果是自己写一个解释程序,工作量的确很大
要看你的平台或编译器,如果你用CB,它有多个第三方的表达式控件可用(如FastScript,支持C/PASCAL/JAVASCript/VBScript或简单的如Rxlib等)
如果你用VC,可以使用ActiveX控件,MicroSoft Scrpit Control 1.0
它也可支持JavaScript及VBScript
它的功能是在你的程序中运行JS/VBS.并可以把结果传给程序的变量

icemornings 2011-12-06
  • 打赏
  • 举报
回复
表达式求值问题……
用表达式树做,或者转成逆波兰表达式再求值,
求值的时候遇到变量就替换成指定数值即可。

---------------
大概就是这个思路吧,不过也没实际做过……

69,373

社区成员

发帖
与我相关
我的任务
社区描述
C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧