从基础到中级:Include 指令·进阶篇
(2/3)· 不少 MQL5 新手硬写几千行重复逻辑,却从不知道一条 #include 就能拆模块
「用静态变量缓存阶乘计算」
在 MQL5 里反复算阶乘会很吃 CPU,尤其放在 OnTick 里每跳都跑的时候。上面这段用 static 变量把上一次的结果留下来,下次调用只做增量乘除,而不是从头算。 核心是两个 static 变量:counter 记录当前算到几的阶乘,value 存对应的阶乘值。初始 counter=0、value=1,相当于 0! 的缓存。
| 当传入的 who 比 counter 小,就反向用除法把 value 除掉多余的项;比 counter 大就用乘法补上。两个 while 循环最多各跑一次差值步数,所以单次调用复杂度是 O( | who-counter | ) 而不是 O(who)。 |
|---|
counter 是 uchar 类型,上限 255,超了会溢出。实盘里若 who 可能大于 255,得把类型换成 ushort 或 int,否则结果会 silently 错掉。
class="kw">static class="type">uchar counter = class="num">0; class="kw">static class="type">ulong value = class="num">1; if (who) who = who - class="num">1; else { counter = class="num">0; value = class="num">1; } while (counter < who) { counter = counter + class="num">1; value = value * counter; }; while (counter > who) { value = value / counter; counter = counter - class="num">1; }; counter = counter + class="num">1; class="kw">return (value = value * counter);
用头文件拆分函数再靠 include 拼回脚本
把一整段脚本里的函数拆到独立文件里,第一招是给每个函数建一个 .MQH 头文件,丢在 Scripts 目录下的子文件夹中。头文件务必用 .MQH 扩展名,这样在系统文件管理器里一眼就能认出是 MQ 语言头文件,而不是普通文本或源码。 拆完之后主脚本会瘦一大圈,看起来比原先清爽得多。但如果你直接按拆分后的样子点编译,编译器会刷出一堆错误,提示它不认识那些过程和函数调用。 原因很简单:编译器只默认认标准库里的保留字、函数和常量,标准库之外你自写的函数,必须显式告诉它去哪找。MQL5 里干这事的就是 #include 编译指令——它相当于在编译时把指定文件整个拉进当前代码上下文。 在脚本开头(技术上也放别处都行,但惯例放头部)加上类似下面的三行,编译就能过: #include "Scripts\\Folder\\File01.mqh" #include "Scripts\\Folder\\File02.mqh" #include "Scripts\\Folder\\File03.mqh" 加上之后重新编译,返回的是成功状态,跑出来的输出和未拆分前完全一致。 文件名和目录怎么摆没有硬规则,你随便定,但 #include 里声明的路径写法有讲究,为什么有时带引号有时带尖括号,文档里「包含文件(#include)」一节有写,先照着用,后面再深挖。 别把正态当圣经 头文件拆分不是为好看,是为让大脚本可维护;但 #include 路径写错一个斜杠,MT5 编译器就不会给你面子,错误列表能瞬间铺满。开 MT5 建两个 .MQH 试一次比读十遍直观。
class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">double One_Radian() { class="kw">return class="num">180. / M_PI; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">ulong Tic_Tac(class="type">bool reset = false) { class="kw">static class="type">ulong Tic_Tac = class="num">0; if (reset) Tic_Tac = class="num">0; else Tic_Tac = Tic_Tac + class="num">1; class="kw">return Tic_Tac; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">ulong Factorial(class="type">uchar who) { class="kw">static class="type">uchar counter = class="num">0; class="kw">static class="type">ulong value = class="num">1; if (who) who = who - class="num">1; else { counter = class="num">0; value = class="num">1; } while (counter < who) { counter = counter + class="num">1; value = value * counter;
◍ 多文件拆函数后怎么在 OnStart 里跑通
把阶乘、弧度、Tic_Tac 这些工具函数拆进 Tutorial 目录下的 .mqh 后,主脚本只需在头部用 #include 挂上三行,就能直接调用,不需要再写一遍逻辑。 下面这段是拆库后的主入口,Factorial(5) 会向日志打印 120,Factorial(3) 打印 6,One_Radian() 返回 1 弧度的正弦近似值 0.84147,这些都是你开 MT5 按 F5 能立刻复现的数字。 do-while 里 Tic_Tac() 不带参时持续自增计数,直到返回值 ≥3 才跳出循环;第 16 行显式传 true 会重置内部计数器,下一次无参调用又从 1 起算。这种状态函数配合头文件分离,适合把指标计算模块和回测驱动脚本分开维护。 外汇与贵金属杠杆高,这类计数逻辑若用于仓位步進或挂单节奏,务必先在策略测试器跑历史数据确认边界,避免计数溢出导致异常批量下单。
class="macro">#include "Tutorial\File class="num">01.mqh" class="macro">#include "Tutorial\File class="num">02.mqh" class="macro">#include "Tutorial\File class="num">03.mqh" class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart(class="type">void) { class="type">ulong value; Print("Factorial of class="num">5: ", Factorial(class="num">5)); Print("Factorial of class="num">3: ", Factorial(class="num">3)); Print(One_Radian()); do { value = Tic_Tac(); Print(__FUNCTION__, " ", __LINE__, " Tic Tac: ", value); }while (value < class="num">3); Print(__FUNCTION__, " ", __LINE__, " Tic Tac: ", Tic_Tac(true)); Print(__FUNCTION__, " ", __LINE__, " Tic Tac: ", Tic_Tac()); }
「把 Tic_Tac 跑进日志里看差异」
上面这段是 Tic_Tac 函数的调用收尾,直接把两种调用方式都打到 MT5 专家日志里。第 20 行传了 true,强制走“滴答优先”分支;第 21 行用默认参数,走常规分支。
在 MT5 终端按 Ctrl+T 打开专家标签页,加载挂了这段代码的 EA,就能看到同一函数两次输出的字符串差异。外汇与贵金属波动受杠杆与消息面影响,日志现象仅反映代码逻辑,不预示任何行情方向。
如果你在日志里发现两次 Tic Tac: 后面的值完全一致,说明默认分支和强制分支在当前品种周期下没分出区别,这时候该去查 Tic_Tac 内部对 true 的处理是否真改了返回值。
class="num">20. Print(__FUNCTION__, " ", __LINE__, " Tic Tac: ", Tic_Tac(true)); class="num">21. Print(__FUNCTION__, " ", __LINE__, " Tic Tac: ", Tic_Tac()); class="num">22. } class="num">23. class=class="str">"cmt">//+------------------------------------------------------------------+