首页 果壳精灵教程 当前文章

【新手入门】第 11 讲:自定义函数(命令)

xLeaves(xxrpa) 发布于 2022年05月27日 22:27:33


果壳精灵提供了数百条命令,功能涵盖了:数学计算、文字处理、数据处理、键盘鼠标模拟、用户界面、时间、文件、进程、网络、软件自动化等很多方面。

一些简单的功能开发使用这些命令组合就可以轻松解决问题了,但随着我们的工程逐渐变大,各个功能模块的代码量也在变多,不可避免的就会出现相同逻辑的代码在多个地方反复使用的问题,这个时候我们就必须要考虑代码复用性的问题了。

解决这个问题的办法是使用函数将需要复用的代码封装起来。


实现一个函数非常简单,都是固定的语法规则,以【Function】开始,以【End Function】结束,定义一下函数的名字和返回值,中间的代码就可以复用了。

例如我们经常需要在调试日志里输出一条分割线,两边加上空行,就可以定义这样一个名为【输出分割线】的函数:

Function 输出分割线() As Long
	TracePrint("")
	TracePrint("------------------------------------ 分割线 ------------------------------------")
	TracePrint("")
End Function

Function Main() As Long
	输出分割线()
End Function

定义好一个函数之后,我们只需要像调用普通命令一样去调用它就可以了,如上面代码所示。

这里的【Function】和【End Function】都是固定的,Function之后跟一个空格,然后写函数的名字,这个名字的命名规则和变量是一样的。

函数名写好之后,后面写一个括号,括号内可以写一些传递给函数的【参数】。


括号后面,跟着 As Long,这种写法大家应该会比较眼熟,在定义变量的章节里,我们定义一个变量的后半部分也是这个写法。

它和定义变量的后半段是一个含义,表示这个函数执行后,会得到一个【返回值】,这个返回值可以直接赋值给某个变量,或者直接作为其他函数的参数使用。

As 后面跟着的是数据类型,任何数据类型都可以作为返回值使用,Long 代表我们的返回值是个整数,如果我们要返回一个字符串的话,将 Long 改成 String 就可以了,以此类推。

设置函数的返回值有三种方法,第一种方法是在函数内使用函数名变量赋值:

Function 输出分割线() As Long
	TracePrint("")
	TracePrint("------------------------------------ 分割线 ------------------------------------")
	TracePrint("")
	输出分割线 = 123
End Function

Function Main() As Long
	输出分割线()
End Function

第二种方法是在函数内使用 Function 关键字赋值:

Function 输出分割线() As Long
	TracePrint("")
	TracePrint("------------------------------------ 分割线 ------------------------------------")
	TracePrint("")
	Function = 123
End Function

Function Main() As Long
	输出分割线()
End Function

第三种方法是在函数内使用 Return 语句:

Function 输出分割线() As Long
	TracePrint("")
	TracePrint("------------------------------------ 分割线 ------------------------------------")
	TracePrint("")
	Return 123
End Function

Function Main() As Long
	输出分割线()
End Function

这里推荐使用 Return 语句的方式来传递返回值,当然,在不希望退出函数的时候(例如返回值需要反复处理的情况下),也可以采用前两种方法。


我们在调用命令的时候可以传递一些参数,自定义的命令也可以设置一些参数,在命令名后面的括号里,按照变量定义的写法写就可以了,这里不需要写 Dim 语句,参数在函数内的使用方法也和变量一样。

例如我们希望提供两个数字类型的变量,可以这样写:

Function 加法(a As Long, b As Long) As Long
	Return a + b
End Function

Function Main() As Long
	TracePrint 加法(1, 2)
End Function

然后我们调用【加法】函数,传递两个数字进去,就可以得到返回值了。

整个逻辑看起来并不复杂,我们将调用函数的第一个参数【1】传递给这个函数的第一个参数【a】,将第二个传递给第二个参数【b = 2】。

然后这两个数据就到了【加法】这个命令内,加法命令可以根据传递进来的参数进行一些加工,最终生成了一个成品,就是【返回值】。

和我们现实生活中的工厂是不是挺像的,参数就是原材料,返回值就是加工后的产品,函数就是加工的生产线。

我们可以选择用锤子敲敲打打,加工一件商品出来,也可以选择用成熟的生产线批量、一致的加工出一些商品出来,这就是零散的函数调用,和封装的区别了。


在传递参数的时候,还可以指定这个参数是传递值还是传递地址,他们分别用【ByVal】和【ByRef】表示,加在参数定义的最前面。

ByVal 表示传递值,ByRef 是传递地址,这两种传递方式的区别在于,ByVal 参数传递后,和原始数据就没有关系了,而 ByRef 传递参数之后,和原始数据其实是同一个数据。

下面的代码会展示两者的区别:

Function 参数类型(ByVal a As String, ByRef b As String) As Long
	a = "Hello"
	b = "GeekBot"
	TracePrint a & ", " & b
End Function

Function Main() As Long
	Dim c As String = "你好"
	Dim d As String = "果壳精灵"
	参数类型(c, d)
	TracePrint c & ", " & d
End Function

第一个输出来自于代码第四行的 TracePrint 命令,它将参数 a 和 b 分别设置了新的值,然后打印出【Hello, GeekBot】。

一开始被传递给函数的【你好】和【果壳精灵】的数据全都被覆盖了,因此不复存在,但这里第二个参数【b】是以 ByRef 形式传递的,因此它和 d 这个变量,可以视为同一个数据,我们在函数内修改了参数 b 的值,参数 d 的值也会被改变。

因此第二个输出的内容,变成了【你好, GeekBot】,可以看到,变量 d 的数据被修改了。

通常这种 ByRef 的传地参数,被称作【形参】,形参全称形式参数,因为一些数据抽象化的原因取得了这个名字,现在我们已经没有必要再去了解这些术语了,学习他们只是为了在和别人交流的时候,不会因为突然蹦出一个没听过的单词而懵掉。

传值和传址我们也可以用工厂的模型来理解,传值就是直接将原材料扔进生产线,传址则是告诉生产线,你要的东西在哪,让生产线自己去取,在取货的过程中,对仓库的操作,是会影响到仓库信息的。


我们还可以定义一个参数为可选参数,这样我们在传递参数的时候,就可以把这个参数忽略掉了。

定义可选参数使用 Optional 关键字,加入到参数定义的前面,例如:

Function 参数类型(ByVal a As String, Optional ByRef b As String) As Long
	a = "Hello"
	b = "GeekBot"
	TracePrint a & ", " & b
End Function

Function Main() As Long
	Dim c As String = "你好"
	Dim d As String = "果壳精灵"
	参数类型(c)
	TracePrint c & ", " & d
End Function

这一次执行的结果产生了很大的变化,GeekBot 没有再替换掉 果壳精灵,因为我们省略了第二个参数,因此第二次输出的内容变成了【你好, 果壳精灵】。

但如果我没不给 b 设置新的值,又会产生不一样的运行结果:

Function 参数类型(ByVal a As String, Optional ByRef b As String) As Long
	TracePrint a & ", " & b
End Function

Function Main() As Long
	参数类型("你好")
End Function

现在它只会输出【你好,】了,因为一个参数可选的话,它的默认值,就是变量定义后不赋值的默认值,在这里,字符串的默认值为【""】(空的字符串),因此后面什么也没有。

如果你希望当这个参数没有传递的时候,可以设置一个别的默认值,只需要像变量定义那样,在参数定义后面,给它一个初始值就可以了:

Function 参数类型(ByVal a As String, Optional ByRef b As String = "果壳精灵") As Long
	TracePrint a & ", " & b
End Function

Function Main() As Long
	参数类型("你好")
End Function

值得注意的是,Optional 语句在函数定义中,只出现一次就可以了,当一个参数被 Optional 语句设置为可选参数的情况下,这个参数后面的所有参数都是可选参数。

另外可选参数的初始值目前还不能设置字符串和结构体类型(结构体类型我们之后会学到)。

因为语法上我们支持多行字符串的写法,无法有效解析参数默认值,可以通过判断参数是否是空字符串来设置默认值。


函数需要定义一个返回值,但有时候我们执行一段代码不需要返回值,这时候可以定义一个子程序。

子程序也叫子过程,本质上就是不带返回值的函数,用于仅需要执行一段代码的情况。

子程序写法如下(将 Function 替换为 Sub,去掉返回值定义即可):

Sub 一个子程序(ByVal 参数 As String)
	TracePrint("子程序执行:" & 参数)
End Sub

Function Main() As Long
	一个子程序("调用")
End Function


最后,让我们做一个小小的练习吧,InputBox 命令返回一个字符串,如果我们希望这个输入对话框只能返回一个数字,该怎么办呢?

你可以自己尝试实现一下,然后对比我给出的答案,看看思路是否相同:

Function InputBoxInt(Optional ByVal sPrompt As String, ByVal sTitle As String, ByVal iDefault As Long = 10) As Long
	If sPrompt = "" Then sPrompt = "请输入一个数字吧:"
	If sTitle = "" Then sTitle = "果壳精灵"
	Dim sRet As String = InputBox(sPrompt, CStr(iDefault), sTitle)
	Return CLng(sRet)
End Function

Function Main() As Long
	TracePrint InputBoxInt()
End Function

若无特殊声明,上述内容为本站原创,未经授权禁止转载!