从基础到中级:定义(一)·进阶篇
(2/3)· 递归之后,定义如何让你用一行指令切换常量、宏与代码版本而不动逻辑
斐波那契数列的两种 MT5 写法对比
在 MT5 脚本里算斐波那契数列,常见有递归和迭代两条路。下面这段脚本默认取第 6 项(def_Fibonacci_Element_Default = 6),OnStart 里同时打印两种算法的返回值,方便你直接开 MT5 跑一遍看差异。 递归版 Fibonacci_Recursive 逻辑直白:arg<=1 时直接返回 arg,否则返回前两项递归之和。代价是重复计算多,第 6 项尚可,项数一大栈开销和耗时就会明显膨胀。 迭代版 Fibonacci_Interactive 用 for 循环做状态累加,c 从 0 起、步长 2,i 与 v 交替进位,最后按 c==arg 决定返回 i 还是 v。同样算第 6 项,它只走三轮循环,效率远高于递归。 把默认宏改成 7 再跑一次,递归结果应为 13、迭代也为 13;外汇与贵金属杠杆高、波动剧烈,此类算法仅作工具验证,实盘信号请自担风险。
class="macro">#class="kw">property copyright "Daniel Jose" class="macro">#define def_Fibonacci_Element_Default class="num">6 class="type">void OnStart(class="type">void) { Print("Result: ", Fibonacci_Recursive()); Print("Result: ", Fibonacci_Interactive()); } class="type">uint Fibonacci_Recursive(class="type">uint arg = def_Fibonacci_Element_Default) { if(arg <= class="num">1) class="kw">return arg; class="kw">return Fibonacci_Recursive(arg - class="num">1) + Fibonacci_Recursive(arg - class="num">2); } class="macro">#undef def_Fibonacci_Element_Default class="type">uint Fibonacci_Interactive(class="type">uint arg = def_Fibonacci_Element_Default) { class="type">uint v, i, c; for(c = class="num">0, i = class="num">0, v = class="num">1; c < arg; i += v, c += class="num">2) v += i; class="kw">return (c == arg ? i : v); }
◍ 斐波那契交互函数的循环骨架
下面这段 MQL5 函数用极简循环生成类斐波那契序列,适合直接塞进指标里做动态层级计算。 传入参数 arg 默认取 def_Fibonacci_Element_Default,循环里用 c 计数、i 与 v 交替累加,每轮 c 加 2,因此 arg 为奇数偶数会走到不同返回分支。 当 c 恰好等于 arg 时返回 i,否则返回 v;这意味着调用 Fibonacci_Interactive(23) 会跑满 12 轮(c 从 0 到 22 再加 2 越界停止),返回的是越界前的 v 值。开 MT5 新建脚本粘贴下方代码,用 Print(Fibonacci_Interactive(23)) 即可核对数值。
class="type">uint Fibonacci_Interactive(class="type">uint arg = def_Fibonacci_Element_Default) { class="type">uint v, i, c; for (c = class="num">0, i = class="num">0, v = class="num">1; c < arg; i += v, c += class="num">2) v += i; class="kw">return (c == arg ? i : v); }
「用编译指令切分斐波那契的两种算法」
在 MT5 里做同一段逻辑的多版本管理,不一定非得靠注释来回删改。MQL5 的 #define 配合 #ifdef / #ifndef / #else / #endif,能让你用一行开关决定编译器走哪段代码。 下面这段是最小可验证样本:第 4 行定义了 DEF_INTERACTIVE,于是第 13 行开始的 #ifndef 块(递归版)会被编译器直接忽略,真正进二进制的是后面 #ifdef 对应的迭代实现。把第 4 行注释掉再编译一次,终端输出就会换成递归版结果——两个版本只差一行,不碰函数体。 [CODE] //+------------------------------------------------------------------+
| // | 控制斐波那契计算版本的最小样本 |
|---|
//+------------------------------------------------------------------+ #define DEF_INTERACTIVE #define def_Fibonacci_Element_Default 6 void OnStart(void) { Print("Result: ", Fibonacci()); } #ifndef DEF_INTERACTIVE uint Fibonacci(uint arg = def_Fibonacci_Element_Default) { if (arg <= 1) return arg; return Fibonacci(arg - 1) + Fibonacci(arg - 2); } #endif #ifdef DEF_INTERACTIVE uint Fibonacci(uint arg = def_Fibonacci_Element_Default) { uint a = 0, b = 1; for(uint i = 0; i < arg; i++) { uint t = a + b; a = b; b = t; } return a; } #endif [/CODE] 逐行拆一下关键点:第 4 行 #define 不需要赋值,存在即可被 #ifdef 捕获;第 13 行 #ifndef 表示「未定义才编译」,所以定义了就跳过递归版;第 25 行 #ifdef 正好相反,定义了才编译迭代版;第 6 行把默认项数 6 做成命名常量,改参只动这一处。 实测时保留第 4 行,默认算第 6 个斐波那契数得到 8;把第 4 行改成 //#define DEF_INTERACTIVE 后重编,递归版对同一个默认值同样返回 8,但调用栈深度差异巨大——递归版在 arg 稍大时(如 30)耗时是指数级,迭代版仍是线性。外汇 EA 里若把这类计算塞进 OnTick,版本选错可能直接拖垮执行性能。 顺带一提,#else 可以嵌在 #ifdef 块内,等价于 C 里 if/else 的编译期版本,省得写两遍对称结构。MQL5 没有 C 的 #if 数值判断,只能测「定义在不在」,所以别指望用 #define 值做条件表达式,老老实实靠有无定义切分支。
class="macro">#define DEF_INTERACTIVE class="macro">#define def_Fibonacci_Element_Default class="num">6 class="type">void OnStart(class="type">void) { Print("Result: ", Fibonacci()); } class="macro">#ifndef DEF_INTERACTIVE class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default) { if (arg <= class="num">1) class="kw">return arg; class="kw">return Fibonacci(arg - class="num">1) + Fibonacci(arg - class="num">2); } class="macro">#endif class="macro">#ifdef DEF_INTERACTIVE class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default) { class="type">uint a = class="num">0, b = class="num">1; for(class="type">uint i = class="num">0; i < arg; i++) { class="type">uint t = a + b; a = b; b = t; } class="kw">return a; } class="macro">#endif
斐波那契函数里的两套实现路径
这段代码在同一文件里用 #ifdef DEF_INTERACTIVE 切出了两套斐波那契计算逻辑,编译时由宏开关决定走哪条路。关掉 DEF_INTERACTIVE 时是经典递归版,开启后则换成迭代版,两者默认都取 def_Fibonacci_Element_Default = 6 作为参数。
递归版在 arg <= 1 时直接返回自身,否则 Fibonacci(arg-1) + Fibonacci(arg-2),逻辑直观但重复计算多。迭代版用 for (c=0,i=0,v=1; c<arg; i+=v, c+=2) v+=i; 滚动累加,最后按 c == arg 判定返回 i 还是 v,避免了函数反复压栈。
实际在 MT5 里新建脚本,把 DEF_INTERACTIVE 注释掉跑一次,终端会打印 Testing 18 及结果 8;保留宏定义再跑,打印 Testing 32 且结果同为 8。外汇与贵金属杠杆高、滑点随机,这类纯算法验证请在策略测试器离线跑,别直接挂实盘。
想看迭代版到底省多少开销,把 def_Fibonacci_Element_Default 改成 30,递归版终端可能卡顿数秒,迭代版依旧瞬时返回——这条差异在写指标实时计算模块时值得记一笔。
class="macro">#define DEF_INTERACTIVE class="macro">#define def_Fibonacci_Element_Default class="num">6 class="type">void OnStart(class="type">void) { Print("Result: ", Fibonacci()); } class="macro">#ifndef DEF_INTERACTIVE class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default) { Print("Testing ", __LINE__); if (arg <= class="num">1) class="kw">return arg; class="kw">return Fibonacci(arg - class="num">1) + Fibonacci(arg - class="num">2); } class="macro">#endif class="macro">#ifdef DEF_INTERACTIVE class="type">uint Fibonacci(class="type">uint arg = def_Fibonacci_Element_Default) { class="type">uint v, i, c; Print("Testing ", __LINE__); for (c = class="num">0, i = class="num">0, v = class="num">1; c < arg; i += v, c += class="num">2) v += i; class="kw">return (c == arg ? i : v); } class="macro">#endif