void函数可以return吗_MQL5从入门到精通【第五章】函数function

8c7d383286bd07cc4c8cda932a765071.png

文档日期:2019年12月7日

讲完了数据类型,操作语句,接着我们把这些元素组合在一起使用。封装起来,成为函数。可供程序内调用,减少冗余代码,提高代码可维护性,降低程序复杂度。

function是一组代码块,用于完成特定动作,比如处理一个订单,调整止损价位等。我们的教程中,会讲到不少自建的函数,用于完成交易相关的动作。MQL5也提供了许多内置函数,从简单的获取订单信息,到复杂的数学运算,都可信手拈来,直接使用。

好比积木,函数精简为一个一个独立的积木块,然后我们使用程序,将独立的积木块搭建成复杂的结构。

抽象出来的函数,一定要精简,要获取订单信息,那好了,我根据需要的传入参数,传入该函数,它给我返回订单信息便是。程序的任意位置,均可调用。

函数一般要有返回值,当然没有返回值也可以,声明的时候冠以void关键字即可。下面举例:

double BuyStopLoss(string pSymbol, int pStopPoints, double pOpenPrice)
{
    // 代码块
}

函数要求传入三个参数,返回一个double类型数据。按照规则写就OK了。注意三个参数都必须填写,是必填项,不能缺省。字符类型的 pSymbol,整型的 pStopPoints,实数 pOpenPrice。

下面我们实现一个功能,根据三个参数,给函数计算返回值。补充函数代码块部分:

double BuyStopLoss(string pSymbol, int pStopPoints, double pOpenPrice)
{
    double stopLoss = pOpenPrice - (pStopPoints * _Point);
    stopLoss = NormalizeDouble(stopLoss,_Digits);
    return(stopLoss);
}

逐句分析。

double stopLoss = pOpenPrice - (pStopPoints * _Point);

使用开盘价格,减去 止损点与货币报价中当前交易品种的大小点的成绩,计算出来的就是止损价格。注意使用了_Point预定义常量。

stopLoss = NormalizeDouble(stopLoss,_Digits);

此处_Digits也是系统预定义常量。定义当前图表交易品种的价格精确度。使用NormalizeDouble格式化小数保留相应的精度。

return(stopLoss);

返回计算后的值stopLoss。这就是函数的返回值。函数执行到此,直接返回,如果后面还有语句,并不执行

所以您看到了,函数体内,也有提前终端执行,跳出函数的方法,就是使用return返回。这与上一章循环中的break与异曲同工之处。

准备好这个函数,我们可以在程序中用一用,体现一下其价值。我们定义一个输入变量,用于与用户交互,让用户输入止损价,然后在onTick事件处理中调用此函数。

onTick 当NewTick事件发生时在EA中调用这个函数,来处理一个新报价。

也就是报价有更新时,调用此事件内的程序。

// 定义输入变量
input int StopLoss = 500;

// onTick事件
double orderPrice = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
double useStopLoss = BuyStopLoss(_Symbol,StopLoss,orderPrice);

注意,函数要先定义再使用。可以在头部定义,如果是公用函数,单独写出来,在顶部引入即可。

## 默认值

如果一个函数,接收5个参数,但是有些并不是经常变化的参数,或者在函数调用的时候,是否能够选填呢,作为可选参数?可以的。在声明函数的形参中,给其默认值,那么在调用的时候,就可以不给这个位置传参。

double BuyStopLoss(string pSymbol, int pStopPoints, double pOpenPrice = 0)
{
    double stopLoss = pOpenPrice - (pStopPoints * _Point);
    stopLoss = NormalizeDouble(stopLoss,_Digits);
    return(stopLoss);
}

上述同样的函数定义,我们在形参中声明pOpenPrice默认=0。那么如果调用BuyStopLoss时,这个位置的参数如果不传,函数体内,pOpenPrice就用0.0这个默认值了。

BuyStopLoss(_Symbol,StopLoss);

就想上面这个一样。

那是不是我只要声明了默认值的参数位置,就可以不填了呢?不可以!可选参数列表放在形参的尾部,这样可变参数在前,不变参数在后,写的时候,你就可以像下面这样用:

double grad(int x1=1,int y1=2,int x2=3,int y2=4){
    return (y2-y1)/(x2-x1);
}

上述函数计算两个点的斜率。坐标点都有默认值,调用的时候,下面的使用方法都是正确的:

grad();
grad(1);
grad(1,2);
grad(1,2,3);
grad(1,2,3,4);

我们再看必选参数在中间的。

double grad(int x1 = 1, int y1, int x2 = 3, int y2 = 4){
    return (y2-y1)/(x2-x1);
}

那么调用的时候,只能有以下写法:

grad(1,2);
grad(7,3,3,5);

也就是第二个参数,是必填的。无论前面的是否可选,前面的也得填。所以,最佳实践就是把可选参数放后面去。

## return 操作符

像上面所说的,如果提前终结函数运行,可以直接return。函数运行到return这个位置,就跳出了。

int ga(double price, bool is_half){
    if(is_half== false) return(price * price);
    else return(price / 2);
    Print('Never exec');
    return(0);
}

因为if-else把两种情况均考虑了,一定会返回一个数值。那么下面的print根本没有机会执行。

## void类型

有时候我们写一个函数,仅仅为了一段功能和动作,可能不不期望有返回值。那么就可以在函数声明前冠以void。

void TradeEmail()
{
    string subject = "Trade placed";
    string text = "A trade was placed on " + _Symbol;
    SendMail(subject,text);
}

这段代码,使用标题和内容发送一封邮件,不期望有返回值,使用void就可以了。函数内大可不必有return操作符。

## 引用传递参数

一般,我们把参数传递给函数,传递的是这个参数的值。参数的值也保持不变。

那么,如果是某个变量,我们想要其在函数处理中改变其数值,怎么办呢?可以使用引用传递。MQL5程序中,数组和结构体,经常用到引用传递reference。

下面的例子,我们引用传递一个结构体给系统函数SymbolInfoTick()。

bool SymbolInfoTick(
    string symbol,
    MqlTick& tick // 引用结构体MqlTick
);

下面是代码中我们的用法:

MqlTick myTick;
SymbolInfoTick(_Symbol,myTick);
Print(myTick.bid);

第一行,使用系统内置结构体声明一个变量myTick。

第二行,系统函数调用后将返回值更改了变量myTick。

第三行,值更改后的myTick打印属性。

下面再举一个例子,我们接收一个空数组,将其进行填充。这个函数就需要引用传递。

void fill(int &a[]){
    ArrayResize(a,3);
    a[0] = 1;
    a[1] = 2;
    a[2] = 3;
}

这样做的好处是,不用返回值了,再赋值给原始函数,省去一大段代码。

调用的时候这样用:

int f[];
fill(f);
Print(f[0]); // = 1

有没有对引用传递有个质的理解了呢?

## 函数重载

面向对象编程中,我们用到很多概念,如接口,抽象类,继承。那么,在继承层级比较深的类内,有些继承的方法在该类内会有特殊的用法,这时候我们需要重写该方法,也称为“重载”。MQL5函数也可使用重载。说白话就是,同一个函数名,参数不同,写两次。你用的时候,编译器根据传入的参数匹配相应的函数。

bool TrailingStop(string pSymbol, int pTrailPoints, int pMinProfit = 0, int pStep = 10);
bool TrailingStop(string pSymbol, double pTrailPrice, int pMinProfit = 0, int pStep = 10);

更改一个参数,函数名保持不变。像下面这样用

bool b;
b = TrailingStop(_Symbol, 549);

系统会判定使用第一个函数,因为第二个参数很显然,int型。像下面这样用

bool b;
b = TrailingSop(_Symbol, 125.89);

系统判定是访问第二个函数,因为第二个参数位置是double型。

这就是重载。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值