函数
本章要点:
- 函数的理解
- 给函数传递参数
- 从函数返回数值
- 递归的函数调用
10.1函数的理解
函数是一定作用的代码块的集合。我们比如可以把计算两个数和写成一个函数,所以函数是具有一定意义的功能代码块,平时我们工作种,习惯性把重复使用到的代码块编写成函数,这样方便阅读和理解,同时也精简了代码量。
函数其实是用作用域,一般函数默认是全局都可以使用,我们也可以限制函数只能在固定的Task或者只能在某个模块里面生效。这个涉及到函数的作用域我会在下一章节和大家一起讨论这个问题。
函数都要有个函数名,函数名和变量名相同规则相同,也就是说是字母和数组的排列组合,当然其中第一个字符必须是字母,我们在命名函数的时候不能太长,也不能太短,我记得以前在学校的时候学校有了老教授,教我们单片机,他写程序命名就喜欢取汉语拼音的手写字母比如下面这个函数“JQRDD”名称就是“机-器-人-断-电”,你会发现看他写的程序,相当费劲,尤其是没有注释的函数简直是灾难,最搞笑的是这老头子,给他的车拍买了个“80C51”的号码。简直没谁了。我建议可以这样命名“Robot_Power_Off”,你也可以不用下划线直接取命名“RobotPowerOff”。好吐槽完那个老头子了。
10.1.1为什么需要函数
函数的主要有点就是,可以在需要该函数的地方执行该函数任意的次数,如果不能把代码函数话,那我们项目都写在Main函数里面,那将是一个巨大的流水账,正如一句话:用时一时爽,重写火葬场。大家好自为之。
函数的另一个有点就是方便开发测试和维护,比如对某一段函数添加一些功能或者修改之前遗漏的Bug,只要函数的输入输出没有变化逻辑没有错误,以前程序的其他部分调用该代码的部分都不会收到影响。当然你可以在修改后的函数名字后面加上一个“V2”,或者“New”,那问题来了,下次再修改了该怎么命名呢?“V3”或者“New_New”,我以前就见多有同事这么写,后来他也发现不对,直接再函数后面谢谢了“年月日”。我当时看到真的和小哪吒一样,“沙子里进了眼睛”。
当然如果你上面的原则都懒得遵守,为了以后的你依旧能看到从前的你写代码,你可以再一个函数的开头进行备注,写下如下几行:
!【日期】某年某日晴
!【作者】我写下如下,函数
!【功能】关机
这样当你看到这段文字的时候,会由衷的知道自己是多么的机制。
10.1.2函数的结构
当我们再ABB RAPID 中写下人生第一段机器人代码的时候,大部分人都是从Main函数开始,“Mian” 就是函数函数名,也就是该函数的函数头,后面跟着的就是函数体,我们的函数。
PROC Main() !函数头
TPWrite "Hi,I am Robot,But not a robot"; !函数体
ENDPROC
ABB RAPID 如果没有返回的函数,就写 “PROC” 和 “ENDPROC”直接,风格和VB的风格很像。
当让我们可以在函数的头里面添加参数,我们也可以让函数返回结果,“PROC”函数是没有返回结果的函数格式,有返回的函数格式是”FUNC”我会在这一章节的下面和大家分享。
10.1给函数传递参数
给函数传递参数的作用是非常重用的,他几乎是编程的一个思想的体验,你给函数传递什么参数,什么类型的参数,对你的整体的程序框架和扩展有着很大的关系,下面我将和大家讨论如何传递参数。
10.1.2数值传递机制
字面上的意思就是我们直接把数值传递给函数,我们把变量、常量直接传递给函数。函数则在函数的内存生成一个实参数据的拷贝。
参考下面的示例:
PROC IncNumber(num input)
Incr input;
TPWrite"Inside the function Inout "Num:=input;
ENDPROC
PROC Main()
VAR num numberTest:=1;
TPWrite"Before Inout "Num:=numberTest;
IncNumber numberTest;
TPWrite"Affter Inout "Num:=numberTest;
ENDPROC
函数实现了一个简单的给输入数据的自增加 1 ,我们分别把输入的变量在函数执行前后和函数内部的结果进行了输出。结果如下图:10-1
图: 10-1
我们可看到函数内容的Input变量发生了变化,但是实际函数外面的本体并没有发生变化。以上便是一个关于函数值传递的演示,无论函数内部发生了什么变化,函数外部的本体都不会发生变化。
这时候可能会有人说了我想改变传入的数值怎么办呢,这也不难,也就是下面我们需要讨论的。
10.1.3引用传递机制
上面讨论到我们值传递,参数的本地没有发生变化,有时候实际编程中需要传递的数值发生变化,我们应该如何操作呢,其实很简单,我们只要在函数的参数数据格式前面添加一个“var” 或者 “Inout” 关键字
程序写成如下的格式:
PROC IncNumber(inout num input)
Incr input;
TPWrite"Inside the function Inout "Num:=input;
ENDPROC
PROC Main()
VAR num numberTest:=1;
TPWrite"Before Inout "Num:=numberTest;
IncNumber numberTest;
TPWrite"Affter Inout "Num:=numberTest;
ENDPROC
我们把上面的代码再运行一遍,会发现神奇的效果。传入的参数本身被修改了,输出的数值变成了自增之后的结果“2”,运行结果如图:10-2
图 10-2
其实实现这个给函数传递的是一个该参数的引用,当参数本身在函数内部发生变化的时候参数的本体也同样发生了变化。
10.1.3多个参数的输入
其实函数参数一次可以给函数输入多个参数,下面我们用刚刚学到的技能编写一个计算的函数,也就是我们传递三个数第一个是加数,第二个是被加数,第三个是结果。
代码如下“:
PROC Main()
VAR num addend1:=1;
VAR num addend2:=2;
VAR num addredult;
Operation addend1,addend2,addredult;
TPWrite valtostr(addend1) + " + "+valtostr(addend2) + " = "+ ValToStr(addredult);
ENDPROC
PROC Operation(num number1,num number2,inout num result)
result:=number1+number2;
ENDPROC
细心的朋友会发现多个参数输入函数的时候是用“,“隔开的,而且调用函数的时候没有哦那个”()“将参数包裹,后面我将告诉大家什么时候应该用”()“。
最后的运行结果如下:图 10-3
图 10-3
10.2函数的输出
有了函数的输入同样就有函数的输出的功能写法。
ABB机器人RAPID编程支持函数的输出功能,像常用的 Num,String,Byte,bool等等甚至用户自定义的数据类型也是可以的。
下面我们把上面写的两个数的加法的函数修改一下,让运算的结果不用通过引用的方式获得,直接把运算结果通过函数输出出来,我们把函数做出如下的修改:
PROC Main()
VAR num addend1:=1;
VAR num addend2:=2;
VAR num addredult;
addredult:=Operation(addend1,addend2);
TPWrite valtostr(addend1) + " + "+valtostr(addend2) + " = "+ ValToStr(addredult);
ENDPROC
FUNC num Operation(num number1,num number2)
return number1+number2;
ENDFUNC
我们可以发现运行之后得到了和上面代码同样的结果,我们其实平时编程就应该遵循这样的机制,一个函数从函数的输入输出直接和函数名称直接可以判断出函数的功能,这样的习惯有助于以后对函数的修改和别人接手你的工作有着很大的帮助,同样大家发现我这次写的函数有几个不同。
第一个就是:我采用了“FUNC“ 和 ”ENFUNC“的关键之包裹了函数本本体。这个其实是有输出函数的编写关键字。
第二个就是:我在”FUNC”关键字后面添加了“num“数据类型的关键字,其实是告诉机器人我的函数输出的格式是”num“类型。
第三个就是:我调用函数的时候采用了“()“而不是直接的,其实让编译器知道我们的调用是有输出的函数,在调用的时候需要 ”()“把传递的参数包裹。
10.3可选参数
有了前面的两个小节的讨论其实可以足够应对我们工作中的大部分情况甚至是99%的情况,但是总有一些情况我们比较棘手,比如我们编写了一个函数,是发送数据给上位机的,以前是通过串口发送,现在客户想要通过网线发送,但是我程序框架都写好了,只要把发送数据的函数单独修改一下就好了,结果到下一个客户要求两个功能都要,还要可以自由切换,这次难道,可能你感觉可以写两套程序,使用需求,但是如果你掌握了下面的编程技巧,你就会发现你的人生从此改变,老板欣赏你,同事尊重你,食堂打饭的阿姨手也不抖了。
下面就是我们的一个比较有意思的编程语法糖,“Swith“关键字,可选参数。
我们依旧用之前的例子,两个数据的相加,现在我们需求改成同样的函数,可以实现相加,同时也能实现计算两个数据的减法,虽然减法是特殊的加法但是在编程的时候我们还是需要告诉机器人该如何进行操作。话不多说直接上代码:
PROC Main()
VAR num addend1:=1;
VAR num addend2:=2;
VAR num addredult;
addredult:=Operation(addend1,addend2);
TPWrite valtostr(addend1) + " + "+valtostr(addend2) + " = "+ ValToStr(addredult);
addredult:=Operation(addend1,addend2subtraction);
TPWrite valtostr(addend1) + " - "+valtostr(addend2) + " = "+ ValToStr(addredult);
ENDPROC
FUNC num Operatin(num number1,num number2switch subtraction)
IF Present(subtraction) THEN
return number1-number2;
ELSE
return number1+number2;
ENDIF
ENDFUNC
运行结果如下图: 10 -4
图 10-4
大家有没有发现神奇的一幕,同样的函数同样的参数却输出了不同的结果。
我在函数的参数部分添加了一个“Switch”的关键字后面仅仅跟着可选参数的名称,大家可以看到我两次调用“Operation”第二次调用的时候后面添加了“subtraction”。这样在调用的时候如果想要计算两个数据的和我就不选该参数,如果想要计算两个数据的差我就选用该参数,这样我们的代码在不影响前面的已经编写好并且调用该函数的情况下,后面编写的代码也有了新的功能,也可以理解给旧的代码注入了新的血液,这样无论老旧版本的函数都不会发生冲突,各自安好。
这时候有杠精或者好些的朋友就会问了,我要加减乘除怎么办啊,你只有加减当然简单啦。
好下面我们就讨论一下多个可选参数出现我们的代码该如何编写。
我们不用着急去编写代码,我们可以线整理一下思路,如果“+ - * /”其实是一组互斥的情况,在我们的需求里面一次只能出现一种情况,那我们这时候就想到了之前讨论的或“Or”,的情况,那我们的代码可以这样编写:
FUNC num Operatin(num number1,num number2switch addition|switch subtraction|switch multiplication|switch division)
IF Present(addition) THEN
return number1+number2;
ELSEIF Present(subtraction) THEN
return number1-number2;
ELSEIF Present(multiplication) THEN
return number1*number2;
ELSEIF Present(division) THEN
return number1/number2;
ENDIF
ENDFUNC
这样我们就实现了一个函数加减乘除的都可以计算的功能,我们通过Present函数判断函数的调用者最后是想要如何计算,通过调用输入的参数进行判断。好的今天的分享就到这里晚安大家:)。