从基础到中级:定义(二)·进阶篇
(2/3)· 还在用函数调用堆叠内存?这篇把#define的第二种玩法拆给你看
◍ 递归与宏两种斐波那契取法
这段 MT5 脚本同时给了递归函数和宏两种算斐波那契数列第 N 项的写法,默认取第 11 项(def_Fibonacci_Element_Default = 11)。 递归版 Fibonacci_Recursive 在 arg<=1 时直接返回 arg,否则返回前两项递归之和,代码直观但重复计算多;宏 macro_Fibonacci_Interactive 用 for 循环迭代,变量 i、v 交替累加,按 arg 奇偶取 i 或 v 输出。 开 MT5 新建脚本粘入下方代码,编译运行后「专家」标签页会先打印递归结果、再打印宏结果,两者应一致(第 11 项 = 89)。外汇与贵金属交易涉及高杠杆,此类代码仅作算法验证,不构成任何交易信号。 别把递归当生产首选 实盘里若需算较深序列,递归指数级调用栈可能拖慢 MT5;宏迭代 O(N) 更稳,调 def_Fibonacci_Element_Default 改项数即可秒验。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Daniel Jose" class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define def_Fibonacci_Element_Default class="num">11 class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnStart(class="type">void) { Print("Result: ", Fibonacci_Recursive()); class="macro">#define macro_Fibonacci_Interactive { \ class="type">uint v, i, c, arg = def_Fibonacci_Element_Default; \ for (c = class="num">0, i = class="num">0, v = class="num">1; c < arg; i += v, c += class="num">2) v += i; \ Print("Result: ", (c == arg ? i : v)); \ } macro_Fibonacci_Interactive; } class=class="str">"cmt">//+------------------------------------------------------------------+ 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=class="str">"cmt">//+------------------------------------------------------------------+
把参数塞进宏里的几种写法
宏本质是编译期原地展开的代码块,不像函数那样有参数类型检查。代码 05 第 14 行把值当函数参数传,第 18 行拿到结果;而同样逻辑的宏在第 4 行就把值写死了,跑出来结果不一样,这就是宏不能随便当函数用的第一个坑。 想让宏接收外部值,得在 #define 里留占位符。代码 06 改完之后,宏调用处能传独立值,但跑起来和代码 02 差不多——只是把第 15 行代码复制到各处,教学可以,实盘基本没用。 真有点用的是代码 07:第 14 行声明 ulong 变量却不初始化,第 16 行通过宏按引用改掉它。注意第 21 行函数要的是 uint,所以第 16 行做了显式转型避免编译器警告。宏在第 9 行改了值,主程序里立刻看得见——这种「点击即联动」的引用传递,用错就是定时炸弹。 宏也能装成函数。代码 08 第 6 行定义的宏在第 11 行当函数用,目的不是提速,是隐藏复杂度让代码能读。片段 01 里那组 datetime 宏就是典型:代码 09 第 13–21 行连续调用,每个宏返回变量名+值的十六进制和十进制,终端图 06 证明 datetime 取值正确。外汇和贵金属 EA 里这么写能少踩类型坑,但宏无编译器护航,高风险操作前务必在 MT5 策略测试器单跑验证。
<span class="number">class="num">01</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">02</span>. <span class="preprocessor">class="macro">#class="kw">property </span><span class="macro">copyright</span> <span class="class="type">class="kw">string">"Daniel Jose"</span> <span class="number">class="num">03</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">04</span>. <span class="preprocessor">class="macro">#define </span>def_Fibonacci_Element_Default <span class="number">class="num">11</span> <span class="number">class="num">05</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="number">class="num">06</span>. <span class="preprocessor">class="macro">#define </span>macro_Fibonacci_Interactive { \ <span class="number">class="num">07</span>. <span class="keyword">class="type">uint</span> v, i, c, arg = def_Fibonacci_Element_Default; \ <span class="number">class="num">08</span>. <span class="keyword">for</span> (c = <span class="number">class="num">0</span>, i = <span class="number">class="num">0</span>, v = <span class="number">class="num">1</span>; c < arg; i += v, c += <span class="number">class="num">2</span>) v += i; \ <span class="number">class="num">09</span>. <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Result: "</span>, (c == arg ? i : v)); \ <span class="number">class="num">10</span>. } <span class="number">class="num">11</span>. <span class="comment">class=class="str">"cmt">//+----------------+</span> <span class="number">class="num">12</span>. <span class="keyword">class="type">void</span> <span class="functions">OnStart</span>(<span class="keyword">class="type">void</span>) <span class="number">class="num">13</span>. { <span class="number">class="num">14</span>. <span class="functions">Print</span>(<span class="class="type">class="kw">string">"Result: "</span>, Fibonacci_Recursive(<span class="number">class="num">15</span>)); <span class="number">class="num">15</span>. macro_Fibonacci_Interactive; <span class="number">class="num">16</span>. } <span class="number">class="num">17</span>. <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
「递归与宏两种斐波那契算法的实测差异」
MT5 脚本里给出了两套斐波那契数计算路径:一个是递归函数,一个是宏展开迭代。两者结果一致,但执行特征和适用场景不同。 递归版本 Fibonacci_Recursive 默认参数取 def_Fibonacci_Element_Default = 11,调用 Fibonacci_Recursive(15) 会输出第 15 项斐波那契数 610。它代码直观,但 arg 每增大 1,函数调用次数近似指数膨胀,arg 超过 30 后可能在 MT5 策略测试器里明显卡顿。 宏 macro_Fibonacci_Interactive 用 for 循环做迭代,传入 14 时打印第 14 项 377。它零函数调用开销,适合嵌进指标实时算回撤位。外汇与贵金属杠杆高,任何数列工具只辅助定位,实际进场仍受突发新闻影响,风险偏大。 下面把核心代码逐行拆开看。
class="macro">#define def_Fibonacci_Element_Default class="num">11 class="macro">#define macro_Fibonacci_Interactive(arg) { \ 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;\ Print("Result: ", (c == arg ? i : v)); \ } class="type">void OnStart(class="type">void) { Print("Result: ", Fibonacci_Recursive(class="num">15)); macro_Fibonacci_Interactive(class="num">14); } 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); }
◍ 宏展开后的斐波那契交互计算
把递归改成宏内联,核心是用 for 循环在编译期展开的等价迭代里推进变量。宏 macro_Fibonacci_Interactive 接收 arg,内部声明 uint v, i, c 三个无符号整型做累加器。 循环初始化 c=0, i=0, v=1,条件 c<arg,每轮 i+=v 且 c+=2,随后 v+=i。当 arg 为偶数时循环正常结束 c==arg,取 i;否则 c 会越过 arg,用三元宏 macro_Ternário 回退取 v。 OnStart 里先以 value=15 调用 Fibonacci_Recursive 打印结果,再跑宏做交互式修正,最后 Print 出 value 验证。外汇与贵金属行情受杠杆影响大,这类代码仅作算法验证,实盘前请在 MT5 策略测试器确认数值边界,避免 uint 溢出导致回测失真。 递归版本 Fibonacci_Recursive 以 def_Fibonacci_Element_Default=11 为默认参数,arg<=1 直接返回,否则双路自调用。15 的递归结果应为 610,可作为你打开 MT5 跑这段脚本的对拍基准。
class="macro">#define def_Fibonacci_Element_Default class="num">11 class="macro">#define macro_Ternário(A, B, C, D) (A == B ? C : D) class="macro">#define macro_Fibonacci_Interactive(arg) { \ 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; \ arg = macro_Ternário(c, arg, i, v); \ } class="type">void OnStart(class="type">void) { class="type">ulong value; Print("Result: ", Fibonacci_Recursive((class="type">uint)(value = class="num">15))); macro_Fibonacci_Interactive(value); Print("Checking: ", value); } 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); }