自定义函数
本章节主要是带大家巩固一些编程中的概念,以养成良好的编程习惯。
在MQL4中,自定义函数既自己定义的函数体,从返回类型上可以大概分为带返回值和不带返回值;从调用类型上可以分为带传入参数和不带传入参数。无返回值类型的函数以void 关键字定义,而其他带返回值的可以是我们常见的int,double,bool,string,datetime这些,返回值也是根据需求来定义特定的数据类型,例如返回订单数选择int类型来定义函数,需要返回价格时选择double类型来定义函数,以此类推,综合起来可以分为4类:
- 无参数、无返回值:这种函数执行一些操作或完成一些任务,但不需要接收任何参数,并且不返回任何结果。例如,一个简单的打印信息的函数可以属于这个类别。
- 有参数、无返回值:这种函数接收一个或多个参数,并执行一些操作或完成一些任务,但不返回任何结果。参数提供了函数执行所需的输入。例如,一个函数计算两个数之和并打印结果可以属于这个类别。
- 无参数、有返回值:不需要接收任何参数,但执行一些计算或逻辑操作,并返回一个结果。返回值可以是任何数据类型,如整数、浮点数、字符串等。例如,一个函数返回当前的时间戳可以属于这个类别。
- 有参数、有返回值:这种函数接收一个或多个参数,并执行一些计算或逻辑操作,并返回一个结果。它同时具备参数的输入和结果的输出。例如,一个函数计算两个数之差并返回结果可以属于这个类别。
自定义函数的用途和优点
自定义函数并非是一个完整EA编程时所需的必备编写方式,但合理使用自定义函数,可以让整个代码逻辑非常清晰,具体优势如下:
- 简化代码结构:自定义函数可以将复杂的操作和计算封装在一个函数中,从而简化主要代码的结构。这使得主要代码更加简洁、易读,并且更容易理解和调试。
- 提高代码复用性:自定义函数可以在不同的上下文中重复使用。当你开发多个策略或指标时,可以编写一次自定义函数,然后在不同的策略或指标中多次调用,避免重复编写相同的代码。
- 提升代码可维护性:通过使用自定义函数,可以将代码逻辑分解为多个独立的函数,每个函数负责一个特定的任务。这种结构化的代码设计使得代码更易于阅读、理解和修改,提高了代码的可维护性。
- 代码重构和优化:自定义函数使得代码重构和优化更加容易。通过将常用的代码块封装为函数,可以在需要的时候对函数进行修改、改进和优化,而不必修改所有调用该函数的代码。
封装自定义函数
这张图是上一期课程中的练习内容,分别在OnInit()函数中初始化数组,在OnTimer()函数中执行获取指定数据的逻辑,这样的写法是很多人的编程习惯。但你有没有想过,一旦代码超几千行的时候,你怎么办?同一个作用域中存在多个复杂的逻辑代码块,在流程控制上是非常混乱的一件事情,稍微不注意就可能造成计算的数据发生错误,或者发生逻辑冲突等情况。
为了解决这种棘手的问题,下列图中,我把上一期的代码块分别封装到两个不同的函数中,通过直接在事件函数内调用该函数头来执行相应的操作,这种方法和之前是等效的。并且一旦发生错误,可以对症下药检查代码块所属的函数,实现代码的网格化管理。同理你可以根据不同的功能实现来创建多个函数,在每一步执行哪一个,像管理流水线一样去管理你的代码。
以上函数中,都是void类型的函数,通常用于实现主逻辑快的模块化,至于是否传入参数,得根据实际情况而定。在实际应用中,还有很多场景可惜再细化,对于我而言,我要做就做到极致,为此我们来细化整个代码的模块化封装过程,开始整活。
1、逻辑拆分
通过细化这段代码,我们得到了五个逻辑区块,包含3个主要逻辑函数,和两个功能性封装函数,每个区块都有特殊的用处,其中包含的了文中开始所包含的几种函数类型。
2、定义两个主要的函数单独来作为初始化和获取数据使用。
3、创建一个DarwObj()函数单独用于图形化输出显示,在这个函数中,我们先编写一个用于创建图形对象所需的循环体,循环次数使用货币对数组的数量。
4、此时我们在创建图形对象的过程中,当然少不了独立的图形对象函数,为了这个图形对象函数可以一次创建后一劳永逸的多次使用,我们也单独进行封装。函数体中我们预留了5个数据位置,用来传入对应的数据参与计算,分别是:对象名称,要显示的内容,横坐标、纵坐标,显示颜色,函数类型没有任何返回值(详细的方式我们后面章节再细说)
5、再次回到刚刚的DarwObj()函数,在循环体内调用刚刚的图形函数创建对象。这里调用了3次,是因为我们要显示的内容分为3组分别是:货币名称,ASK价,BID价格,其中每一组都包含了每个货币的对应属性,所以必须借助循环的方式来创建对象。
6、显示判断:在上面调用的图形对象行数中,5个参数分别对应的功能如下图,其中,我们又嵌套了一次调用函数setclr(symbol[i]),用于根据特殊需求显示颜色。
因此我们创建了一个用于返回显示颜色的函数,该函数包含一个字符串的参数传入。
功能解析:传入循环时的货币名称,如果货币名称是EURUSD,返回红色作为显示颜色,否则(如果不是EURUSD)则返回绿色作为显示颜色。
到这里我们的代码全部重构完成如下所示,
在三个主要函数中,我们分别放入了对应的事件函数内,为啥其余两个不放入?因为其他两个属于功能性函数,只在需要的时候调用它,在本段代码中,剩余了两个函数只有DarwObj()需要,所以这里不放。解读:初始化时调用一次ArrayInit()函数用于初始化数组,然后在OnTimer()函数中根据计时器间隔更新获取数据,紧接着调用DarwObj()函数创建对象。
看效果:(颜色判断已经根据函数判断中的EURUSD做了区分显示)
参数传递与返回概念
熟悉了刚刚的流程,我们在继续巩固一下参数传递与返回参数的概念,所谓的传递参数就是在调用函数的时候把函数调用后计算的过程需要的数值传递给函数体本身;返回则是函数把计算好的数值再返回给调用它的语句。
下图中,函数2在内部变量add1调用了函数1,并按照约定格式传入了两个整型值作为函数2中加法运算所需的值,然后通过运算后返回运算结果给函数2中的变量add1。
函数调用顺序
函数创建的顺序可以随意,但调用顺序必须讲究先来后到。这里不要去纠结先有鸡还是先有蛋的问题,你只需要知道,你调用一样东西得先确定存在的事实,比如你要调用计算好的订单利润,那你就得先计算出列润数据,才到调用的环节,讲究先来后到,或者是先检查一遍是否存在再调用;再比如你要实现孵小鸡的过程,就先得把鸡蛋获取到,这是一个在使用自定义函数时必须牢记的事情。