CalcStar是一个可扩展的、快速的C++函数计算器,它有着像科学计算器一样强大的计算能力。它的库可以在任何C++工程中使用,提供函数计算功能。这个函数计算器将输入符号化并将其编译如你个RPN栈中,之后计算出结果返回给用户。式子用C++句式输入,并使用中缀表达式(例 2+2)。CalcStar支持带变量的式子。用下面的代码使用CalcStar的库函数。
#include <CalcStar.h>
然后,用户定义一个函数计算器,设定待计算表达式和变量,然后计算得到结果。当一个等式被计算后,用户可以重复利用现有的RPN栈来避免重复的计算。不过应当知道,计算其实是非常快的。下面是一个函数计算器到的使用实例,在源码中的TestApp中也可以找到。
Std::string strFunc; strFunc = "(2 + 2)/3 * pow(3,2) - 2"; this->m_objFuncEvaluator.Set_strExpression(strFunc); m_objFuncEvaluator.Evaluate(); dblAnswer = m_objFuncEvaluator.Get_dblCurrValue();
像这样简单地设置变量:
CSVariable objVarTemp; objVarTemp.m_strVarName = "X1"; objVarTemp.m_dblCurrVal = varValue; m_objFuncEvaluator.AddVariable(objVarTemp);
同样便利的变量赋值:
m_objFuncEvaluator.UpdateVariable(strVarName, dblNewVal);
CalcStar是通用计算器。基本上他可以与任何中缀表达式科学计算器媲美(就像TI-83)。CalcStar计算表达式三步走:符号化、编译、执行。在符号化阶段,string中的数学表达式被转换成符号或数学语言。符号是数学表达式中的基本单元。下面是CalcStar中的符号(Token)类型:
enum CSTAR_DLLAPI CSTokenType { //IMPORTANT NOTE!!! these operators are // in order of operation precedence //THE LOWER THE NUMBER THE HIGHER THE PRESCEDENCE //DO NOT CHANGE THIS ORDERING //HARDCODING IS USED BASED ON ENUMERATED TYPES // NOOP = -1, OPENPAREN = 0, //Open Parenthesis OPENBRACKET, //Open Bracket OPENBLOCK, //Open Block CLOSEPAREN, //Close Parenthesis CLOSEBRACKET, //Close Bracket CLOSEBLOCK, //Close Block COMMA, //Comma separator NUMBER, //Number VAR, //Variable ASSIGN, // = Assignment Operator NEG, // - Negative Operator ADD, // + Addition Operator SUB, // - Subtraction Operator MULT, // * Multiplication Operator DIV, // / Division Operator LT, // < Less Than Operator GT, // > Greater Than Operator LTE, // <= Less Than or Equal Operator GTE, // >= Greater Than or Equal Operator NEQ, // != Not Equal To Boolean EQ, // == Equal To Boolean AND, // && Logical And Boolean OR, // || Logical Or Boolean NOT, // ! Not Operator FUNC // Function };
函数(Function)可以是任何类型的函数,可以被用户定义,例如aspow()是幂函数。函数的后面必须跟有"("。应当注意,如果CalcStar没有在已知函数中找到式子中的函数,它将把该函数标记是做一个变量。CSToken类有着下面这些成员变量:
//This is a thin class, so member variables are public //like in a typical struct //!The token type of this RPN Unit CSTokenType m_objTokenType; //!The string representation of the token, also the signature std::string m_strToken; //!Token's associativity CSAssociativity m_objAssociativity; //!Numeric version of token if applicable double m_dblToken;
表达式被符号化后,就可以将其编译入逆波兰表达式(RPN)栈中,然后进行计算。使用Shunting Yard算法(一个计算机科学中的著名算法)创建RPN栈。RPN栈保存了运算顺序,并可以让计算机简单地通过扫描栈来计算式子。一个运算完成后,它的结果被保存为栈中单一的CSRpnUnit。不断执行该过程直到栈中只剩一个数字。这就是表达式的结果。
用户可以输入任何可得出一个数的C++风格的中缀数学表达式。一个例子是:( 2 + 3) * X – 1,这里X是一个等于3.1415926的变量。唯一的危险是负数应当用括号括起来,例如(-2)。这将会在后续版本中解决。这个错误的原因是CalcStar将负号视作减号,而并没有将其视作数的一部分。在变量中必须保留负号,因为在编译时还不知道变量的值。另一个需要注意的是减号必须跟一个空格,否则将被视作负号。CalcStar内建的运算和函数如下:
Basic Functions | |||
Name | Signature | Name | Signature |
Add | + | Boolean And | && |
Subtract | - | Boolean Equal | == |
Multiply | * | Boolean Not | ! |
Divide | / | Boolean Not Equal | != |
Exponential | exp | Boolean Or | || |
Power | pow | Greater Than | > |
Natural Log | ln | Greater Than or Equal | >= |
Log2 | log2 | Less Than | < |
Log10 | log10 | Less Than or Equal | <= |
Absolute Value | abs | Ceiling | ceil |
Square Root | sqrt | Floor | floor |
Truncate | trunc | ||
Trigonometric Functions | |||
Name | Signature | Name | Signature |
Sine | sin | Arc Sine | asin |
Cosine | cos | Arc Cosine | acos |
Tangent | tan | Arc Tangent | atan |
Hyp Sine | sinh | Arc Hyp Sine | asinh |
Hyp Cosine | cosh | Arc Hyp Cosine | acosh |
Hyp Tangent | tanh | Arc Hyp Tangent | atanh |
CalcStar的一个重要特性是它是可扩展的。用户可以轻松地使用已有的运算和函数添加自定函数。每一个CSRpnUnit都有一个指向CSOpBase的指针,CSOpBase负责在Evaluate()被调用时执行运算。CSOpBase是所有运算和函数的基类。这是一个基本的伪函数设计,有一个虚函数:
virtual int OpEval(std::vector<CSRpnUnit> & arrObjCalcStack, bool & blnCalcSuccessful,int intCurrPos) = 0;
该函数在被继承之后重载。我们可以从CSOpPower函数中一探究竟:
class CSOpPower: public CSOpBase { public: //!Default Constructor CSOpPower(); //!Default Destructor ~CSOpPower(); //!Perform the operation on the stack virtual int OpEval(std::vector<CSRpnUnit> & arrObjCalcStack, bool & blnCalcSuccessful,int intCurrPos); }; //OBJECT FACTORY REGISTRATION CODE static bool blnCSOpPower_Registered = CSTAR::GetOpBaseFactoryPtr()->Register<CSOpPower>("pow");
CSOpPower继承自CSOpBase,并实现了虚函数OpEval来进行乘方计算。在定义了类之后,新的方法通过GetOpBaseFactoryPtr()…这行代码注册到OpBase工厂类。提供给函数的string是该函数的符号。像这样,用户想要注册一个新函数只要注册就可以了。当库文件被编译时,它会注册所有函数。 OpEval的实现:
//!Perform the operation on the stack int CSOpPower::OpEval(std::vector<CSRpnUnit> & arrObjCalcStack, bool & blnCalcSuccessful,int intCurrPos) { //FUNCTION: pow(Number, numdecimal) //first determine if the necessary inputs are valid, if they are, //then get the input numbers and perform the calculation, // Once the calculation is made // then replace the operation and input tokens // with the output number token char chrOutput[256]; for (int i = 0; i < 256; chrOutput[i++] = ' '); blnCalcSuccessful = true; bool blnValid; double dblOutput; CSRpnUnit objOutput; try{ this->ValidOpInputs(arrObjCalcStack,blnValid,intCurrPos,2); if(blnValid) { //then valid inputs, perform the power calculation //then replace the sin calculation results with the single number double dblNum, dblPower; this->GetOpNumber(&(arrObjCalcStack.at(intCurrPos - 1)),dblPower); this->GetOpNumber(&(arrObjCalcStack.at(intCurrPos - 2)),dblNum); dblOutput = pow(dblNum,dblPower); objOutput.m_objTokenType = NUMBER; objOutput.m_dblToken = dblOutput; sprintf_s(chrOutput,"%f",dblOutput); objOutput.m_strToken = chrOutput; this->ReplaceOp(arrObjCalcStack,intCurrPos - 2,intCurrPos,objOutput); blnCalcSuccessful = true; return 1; }else{ blnCalcSuccessful = false; return 0; } }catch(...){ blnCalcSuccessful = false; return -1; }//end try catch };
这个运算首先调用aValidOpInputs()来检查输入的正确性。如果输入合法,就会执行乘方运算。接下来,ReplaceOp()会执行并将输入的数字和运算替换为一个输出数CSRpnUnit。建议用户也用采用这种模式(检查、计算、替换)。也推荐直接复制一个已有的函数,并改变它的名字和函数体来创建新函数。这比较简单,而且也不容易忘事或犯错。
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。 2KB翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。2KB项目(www.2kb.com,源码交易平台),提供担保交易、源码交易、虚拟商品、在家创业、在线创业、任务交易、网站设计、软件设计、网络兼职、站长交易、域名交易、链接买卖、网站交易、广告买卖、站长培训、建站美工等服务