在自动执行交易任务时,可能需要在其执行阶段提供计算算法的灵活性。 例如,当微调程序以闭合(编译)模式分布时,我们可以从众多可能的组合中选择目标函数类型。 特别是在优化智能交易系统或快速评估指标原型时,这很有用。 除了更改对话框中的参数之外,用户还可以更改计算公式。 在这种情况下,我们只需从其文本表达形式计算其数学表达式,而无需更改 MQL 程序代码。可以通过各种解析器来解决此任务,这些解析器可以即时解释公式,将其“编译”为语法树,生成所谓的字节码(计算指令序列),进而执行从而得出计算结果 。 在本文中,我们将研究几种类型的解析器和表达式计算方法。解析器基类(AbstractExpressionProcessor 模板)这是模板类,由于表达式分析结果不仅可以是标量值,而且还可以是描述表达式语法的节点树(特殊类的对象)。 稍后我们会研究如何完成此操作,以及这样做的目的是什么。首先,类对象存储表达式(_expression),其长度(_length),读取字符串(_index)时的当前光标位置,和当前符号(_token)。 它还预留了指示表达式中错误的变量(_failed),和数值比较精度(_precision)。 template<typename T> class AbstractExpressionProcessor { protected: string _expression; int _index; int _length; ushort _token; bool _failed; double _precision;提供了存储变量和链接的特殊表,但是稍后我们将研究相关的 VariableTable 和 FunctionTable 类。 VariableTable *_variableTable; FunctionTable _functionTable;这些表是由 “key=value” 对组成的关键字=数值对,其中关键字是变量或函数名称的字符串,而数值则是 double(对于变量),或 functor 对象(对于表) 。变量表由引用描述,因为表达式不能包含变量。 对于函数表,解析器始终含有最少的内置函数集合(用户可以扩展),这就是为什么此表由已有对象表示的原因。标准函数表是在方法中填充: virtual void registerFunctions();下一章节介绍的函数执行的子任务通用于各种解析器,例如切换到下一个字符,对照期望值检查字符(如果不匹配则显示错误),顺序读取满足格式要求的数字, 以及一些用于字符分类的静态辅助方法。 bool _nextToken(); void _match(ushort c, string message, string context = NULL); bool _readNumber(string &number); virtual void error(string message, string context = NULL, const bool warning = false); static bool isspace(ushort c); static bool isalpha(ushort c); static bool isalnum(ushort c); static bool isdigit(ushort c);所有这些函数都在基类中定义,尤其是: template<typename T> bool AbstractExpressionProcessor::_nextToken() { _index++; while(_index < _length && isspace(_expression[_index])) _index++; if(_index < _length) { _token = _expression[_index]; return true; } else { _token = 0; } return false; } template<typename T> void AbstractExpressionProcessor::_match(ushort c, string message, string context = NULL) { if(_token == c) { _nextToken(); } else if(!_failed) // prevent chained errors { error(message, context); } } template<typename T> bool AbstractExpressionProcessor::_readNumber(string &number) { bool point = false; while(isdigit(_token) || _token == '.') { if(_token == '.' && point) { error("Too many floating points", __FUNCTION__); return false; } number += ShortToString(_token); if(_token == '.') point = true; _nextToken(); } return StringLen(number) > 0; } |