333 lines
12 KiB
C++
333 lines
12 KiB
C++
/********************************************************************
|
|
创建日期: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 <afxwin.h>
|
|
#include <afxtempl.h>
|
|
|
|
//将函数操作符作为单目运算符,运算级别在括号之后
|
|
#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,详见<<PDSUnit.h>>
|
|
int m_iPrecision; //单位精度,用于转换成字符串时使用,默认-1,详见<<PDSUnit.h>>
|
|
|
|
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<CPDSFormulaItem,CPDSFormulaItem&> 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<CPDSMeasurePos,CPDSMeasurePos&> 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<int,int>,CList<int,int> >& 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
|