/******************************************************************** 创建日期:2014/04/14 文 件 名:PDSFormula.h 原始作者:孙宇飞 描 述:本文件中的函数用来解析CString类型的公式,以链表形式保存,具体使用方法是: (1)声明一个CPDSFormula对象,按照CPDSFormula::SetFormula中所列的参数设置,如果不调用该接口,需要手动将各变量设置完整 (2)设置完成后调用CPDSFormula::AnalyseString完成对字符串的解析,结果被保存在m_listFormulaItem中,并且指明了哪些数字项表示长度,哪些表示常量, 对于长度项,m_dConstNumber保存的就是内部长度值,对于常量项,m_dConstNumber保存的就是实际值, 特别说明,对于[-胸围/4]这样的公式将会被解析为[0-胸围/4], 在该步骤中将会形成m_listCalculateFormula链表,它记录了如何将公式计算成数值 (3)计算某号型实际值时,要遍历m_listCalculateFormula链表,将该号型的人体尺寸长度/常量设置到m_dConstNumber中,再调用CalculateResult,其返回值就是结果 注释: (1)主体采用PDS中解析公式的算法,仅对类名称/成员变量/函数名/宏定义进行一些改动 (2)语义分析函数CPDSFormula::SemanticAnalysis是本次新增,它将会指定哪些数字项是内部长度,哪些数字项是常量 算法:假设公式中含有5个数字项,首先假设这5项均为常量,检查各项是否与m_iFormulaType保持一致,如果不一致,假设有4项为常量,共分5种情况,再检查哪一种情况下的公式与m_iFormulaType保持一致,如此再计算3项2项1项0项常量的情况 (3)公式中区分m_listLengthVariableName与m_listAngleVariableName,前者代表长度,后者代表角度,即常量,设置时一定要将不同类型的人体尺寸/尺寸变量字符串写入到相应的链表中 (4)所有的成员变量都是public类型,方便获取数据,要按照上述的使用方法进行操作,没有列出的函数/变量不可设置 修改记录: 版本号 修改日期 作者 修改内容 *********************************************************************/ #ifndef PDSFORMULA_H #define PDSFORMULA_H #include #include //将函数操作符作为单目运算符,运算级别在括号之后 #define PDS_F_OT_NULL 0x00000000 // #define PDS_F_OT_ADD 0x00000001 //+ 加 #define PDS_F_OT_SUB 0x00000002 //- 减 #define PDS_F_OT_MULT 0x00000004 //* 乘 #define PDS_F_OT_DIV 0x00000008 /// 除 #define PDS_F_OT_LBRACKET 0x00000010 //( 左括号 #define PDS_F_OT_RBRACKET 0x00000020 //) 右括号 #define PDS_F_OT_SQR 0x00000040 //√ 开方 //下面的操作符使用字符串表示 #define PDS_F_OT_SIN 0x00000080 //sin #define PDS_F_OT_COS 0x00000100 //cos #define PDS_F_OT_TG 0x00000200 //tg #define PDS_F_OT_CTG 0x00000400 //ctg #define PDS_F_OT_ARCSIN 0x00000800 //arcsin #define PDS_F_OT_ARCCOS 0x00001000 //arccos #define PDS_F_OT_ARCTG 0x00002000 //arctg #define PDS_F_OT_ARCCTG 0x00004000 //arcctg #define PDS_FORMOULA_STACK_SIZE 100 #define PDS_FOT_MAX_LENGTH 7 //使用字符串表示的操作符中字符串的最大长度 //公式错误信息 enum PDS_ERROR_INFO { NoError =0, //没有错误 StackOverflow =1, //栈溢出 ExpressionError =2, //表达式错误(操作符的操作数个数不正确) NOLBracket =3, //只有右括号,没有左括号 NORBracket =4, //存在没有与左括号匹配的右括号 DivideZero =5, //除数为0 SqrtError =6, //开方数小于0 ArcsinError =7, //反正弦数据错误(数据必须在[-1,1]之间) ArccosError =8, //反余弦数据错误(数据必须在[-1,1]之间) StringError =9 //字符串存在错误 }; //公式项 class CPDSFormulaItem { public: int m_iType; //=1 操作符(默认), =2 引用m_pData1/m_pData2, =3 内部长度, =4 常量 int m_iOperatorType; //操作符在m_iType=1时有效,取值范围PDS_F_OT_NULL~PDS_F_OT_ARCCTG int m_iUnit; //单位,在角度公式下,该变量为-1,m_bOperator=FALSE时有效,默认-1,详见<> int m_iPrecision; //单位精度,用于转换成字符串时使用,默认-1,详见<> double m_dConstNumber; //常量数值,既可以作为系数也可以作为常变量,单位为m_iUnit,当单位为英寸分数表示时,将分数转换成小数保存 void* m_pData1; //指针1,可根据实际需要指定 void* m_pData2; //指针2 CString m_strText; //字符串,可根据实际需要指定 int m_iIndex; //记录this在链表中的索引值 int m_iPower; //记录this的幂次,若为长度,该值为1,否则为0 public: CPDSFormulaItem(void); CPDSFormulaItem(CPDSFormulaItem& rhs); ~CPDSFormulaItem(void); CPDSFormulaItem& operator=(CPDSFormulaItem& rhs); BOOL operator==(CPDSFormulaItem& rhs); BOOL operator!=(CPDSFormulaItem& rhs); void Init(void); }; typedef CList CPDSFormulaItemList; //分析字符串时使用到的类 class CPDSMeasurePos { public: CString m_strMeasureName; //在公式中人体尺寸/尺寸变量的名称 int m_iMeasureIndex; //名称在公式中开始的位置 public: CPDSMeasurePos(void); CPDSMeasurePos(CPDSMeasurePos& rhs); ~CPDSMeasurePos(void); CPDSMeasurePos& operator=(CPDSMeasurePos& rhs); BOOL operator==(CPDSMeasurePos& rhs); }; typedef CList CPDSMeasurePosList; class CPDSFormula { public: BOOL m_bOldFormoula; //=TRUE 旧公式, =FALSE 新公式 int m_iFormulaType; //公式类型, =1 长度公式(默认), =2 角度公式 int m_iDPMM; //长度数据单位:dpmm(每毫米点),用来将字符串计算成内部单位 int m_iUnit; //创建公式时,公式所属长度单位 int m_iPrecision; //与单位对应的长度精确度 CString m_strFormula; //用户输入的公式字符串 int m_iMantissa; //旧公式中尾数对应的系统单位值,目的是为了防止单位转换时出现数据误差,只在旧公式中有效 CStringList m_listLengthVariableName; //表示长度的变量名称 CStringList m_listAngleVariableName; //表示角度的变量名称 CPDSFormulaItemList m_listFormulaItem; //记录通过字符串得到的公式项链(公式项在链表中的顺序与公式字符串一致) CPDSFormulaItemList m_listCalculateFormula; //存放公式项计算的顺序,用于计算结果 PDS_ERROR_INFO m_ErrorInfo; //公式错误信息 //分析过程中使用的变量 WORD m_iPositionIndex; //分析字符串过程中,指向字符串的当前位置 CPDSFormulaItem m_CurrFormulaItem; //当前分析到的单词 CPDSMeasurePosList m_listMeasurePos; //分析字符串时,字符串中的人体尺寸和尺寸变量名称及其在字符串中位置 //两个栈变量只用于分析字符串的过程 CPDSFormulaItem m_arrFIStack1[PDS_FORMOULA_STACK_SIZE]; //分析过程中存放操作符 int m_iStackTop1; //0..ParserStackSize;当前栈顶位置 CPDSFormulaItem m_arrFIStack2[2 * PDS_FORMOULA_STACK_SIZE]; //分析过程中存放操作数和操作符 int m_iStackTop2; //0..2 * ParserStackSize;当前栈顶位置 public: CPDSFormula(void); CPDSFormula(CString strFormula, int iDPMM, int iUnit, int iPrecision, BOOL bOldFormoula, int iFormulaType, CStringList& listLengthVariableName, CStringList& listAngleVariableName); ~CPDSFormula(void); void Init(void); //设置函数 void SetFormula(CString strFormula, int iDPMM, int iUnit, int iPrecision, BOOL bOldFormoula, int iFormulaType, CStringList& listLengthVariableName, CStringList& listAngleVariableName); //判断公式是否正确 //返回值: // =TRUE 公式正确,=FALSE 公式错误 //说明: // 判断公式中包含的长度尺寸/角度尺寸是否可以在给定的链表中找到 BOOL IsFormulaRight(void); //设置变量名称链表 void SetVariableName(CStringList& listLengthVariableName, CStringList& listAngleVariableName); //将输入的字符串分析成一个一个单词 void AnalyseString(void); //设置计算链m_listCalculateFormula void SetCalculateList(void); //公式项入栈 void Push1(CPDSFormulaItem Item); //公式项出栈 void Pop1(CPDSFormulaItem& Item); //公式项入栈 void Push2(CPDSFormulaItem Item); //判断给定的两个公式项中操作符的优先级 // [输入]Item1,Item2两个公式项 //返回值: // =0 Item1优先级高于Item2; =1 Item1优先级等于Item2; =2 Item1优先级低于Item2 //说明: // 该函数中不对公式项是否为操作符进行判断 int JudgePRI(CPDSFormulaItem Item1, CPDSFormulaItem Item2); //判断给定的公式项中操作符是单目运算还是双目运算() // [输入] Item 公式项 //返回值: // =TRUE Item为双目运算符; =FALSE Item单目运算符,即函数运算符 BOOL IsDualOperator(CPDSFormulaItem Item); //从字符串中得到每个公式项 //返回值: // =TRUE 表示字符串正确,=FALSE 表示字符串存在错误 // 公式项存放在m_listFormulaItem中 //说明: //思路: //1. 先找出字符串中所有的人体尺寸和尺寸变量,并记录下人体尺寸/尺寸变量名称和在字符串中的位置; //2. 在分析字符串时,先通过位置索引在m_listMeasurePos寻找,到直接返回,找不到通过字符串一个一个分析 //3. 保证第一个公式项为数,并且操作数和操作符(加减乘除)交替 BOOL GetStringFormulaItem(void); //找出m_strFormula中包含的尺寸名称,将结果输出到listMeasurePos中 void GetMeasureInfo(CPDSMeasurePosList& listMeasurePos); //得到字符串中的单个单词,即变量名,操作符,数据 //返回值: // 0 字符串错误,即不是变量名也不是操作符 // 1 到了字符串结尾 // 2 碰到错误字符如:! // 3 返回值为常量 // 4 返回值为变量 // 5 返回值为符号操作符 // 6 返回值为字符串操作符 //说明: // 变量名不能是数字开头 // 只有在单位为英制分数的情况下,才进行分数的分析 int NextFormulaItem(void); //根据索引在m_listMeasurePos中查找对应的MeasurePos //参数: // iIndex 索引 // MeasurePos [输出]找到对应的变量位置,返回值为TRUE时有效 //返回值: // =TRUE 找到, =FALSE 没有找到 //说明: // 如果找到将找到的信息从m_listMeasurePos移出 // 修改如果同一个位置存在多个名称,取名称最长的 // 人体尺寸/尺寸变量名称存在数字 BOOL FindMeasureIndex(int iIndex, CPDSMeasurePos& MeasurePos); //判断是否为函数:sin, cos ,tg, ctg, arcsin, arccos, arctg, arcctg //参数: // strOperatorName [输出]操作符名称 // iOperatorType [输出]操作符类型 //返回值: // =TRUE iOperatorType有意义, =FALSE iOperatorType不能使用 BOOL IsFunc(CString &strOperatorName, int &iOperatorType); //判断是否为变量 //参数: // strVarName [输出]变量名 //返回值: // =TRUE strVarName有意义,=FALSE strVarName不能使用 BOOL IsVar(CString &strVarName); //判断下一个字符是否为操作符 //参数: // strOperatorName [输出]操作符名称 // iOperatorType [输出]操作符类型 //返回值: // =TRUE iOperatorType有意义, =FALSE iOperatorType不能使用 //说明: // 开方在该函数中判断,开方表述方式使用的是符号√ BOOL IsOperator(CString& strOperatorName, int& iOperatorType); //查找给定的名称是否为人体尺寸 //返回值: // =TRUE 找到变量, =FALSE 没有找到变量 BOOL FindVariableForName(CString strVarName); //语义分析 //说明: // 对解析后的m_listFormulaItem进行语义分析,确定其中哪些数字表示内部单位,哪些表示常量 void SemanticAnalysis(void); //设置幂次 //参数: // listFormulaItem 公式项链表 // iPower 幂次,=0 表示常量, =1 表示长度, = 2表示长度*长度,以此类推 void SetPower(CPDSFormulaItemList& listFormulaItem, int iPower); //检查给定链表的幂次是否等于给定值 //参数: // listFormulaItem 链表 // iPower 幂次, =0 表示常量, =1 表示长度, =2 表示长度*长度,以此类推 //返回值: // =TRUE 正确, =FALSE 错误 BOOL CheckPower(CPDSFormulaItemList& listFormulaItem, int iPower); //判断给定链表中是否含有括号 //参数: // listFormulaItem 公式链表 // iIndexL [输出]最左边的括号在链表中的索引值 // iIndexR [输出]与iIndexL对应的右括号在链表中的索引值 //返回值: // =TRUE 有括号,(iIndexL,iIndexR)数据有效, =FALSE 没有括号,输出变量无效 BOOL HaveBracket(CPDSFormulaItemList& listFormulaItem, int& iIndexL, int& iIndexR); //排列组合 //参数: // iMaxCount 总数 // iCount 被提取出的数量 // alIndex [输出]排列组合链表,其中的alIndex[].GetCount()=iCount,索引值从0开始,从大到小排列 void Permutation(int iMaxCount, int iCount, CArray,CList >& alIndex); //获取给定链表的幂次,如果计算失败则返回INT_MAX //特别说明:什么是计算失败? // 例如:A*B+C由加号连接的每一项幂次不等,sqrt(A+B)用来开方的数据不是常数或者2的整数倍,sin(A)三角函数的参数不是常量,等等 //返回值: // 幂次,=0 表示常量, =1 表示长度, = 2表示长度*长度,以此类推 int GetPower(CPDSFormulaItemList& listFormulaItem); //计算结果 //特别说明: // 调用本函数之前,要将设置m_listFormulaItem链表,将人体尺寸的实际值设置到m_dConstNumber中,切记!!! //返回值: // 如果this是长度公式,那么返回值表示长度,四舍五入后可即为内部长度,如果this是角度公式,那么返回值就是角度 double CalculateResult(void); //计算旧公式 double CalculateOldFormula(void); //计算新公式 double CalculateNewFormula(void); //双目操作符运算 //参数: // iOperator 操作符 // dValue1 第一操作数 // dValue2 第二操作数 //返回值: // 结果项(数据公式项) CPDSFormulaItem CalculateValue1(int iOperator, double dValue1, double dValue2); //单目操作符运算 //参数: // iOperator 操作符 // dValue1 第一操作数 //返回值: // 结果项(数据公式项) CPDSFormulaItem CalculateValue2(int iOperator, double dValue1); }; #endif // PDSFORMULA_H