加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

魔兽世界编程宝典读书笔记(5)

发布时间:2020-12-14 22:23:13 所属栏目:大数据 来源:网络整理
导读:? 第5章 ???????????? 高级函数和控制结构 5.1 ??????? 多值返回 在Lua中,return语句能返回多个值,这些值能让我们更轻松地完成一些工作。例如在WoW中,我们有时必须把十六进制的字符串转化为红绿蓝三色十进制值。 5.2 ??????? 将十六进制转换成RGB 十六进

?

第5章???????????? 高级函数和控制结构
5.1??????? 多值返回
在Lua中,return语句能返回多个值,这些值能让我们更轻松地完成一些工作。例如在WoW中,我们有时必须把十六进制的字符串转化为红绿蓝三色十进制值。
5.2??????? 将十六进制转换成RGB
十六进制字符串的典型例子是“FFCC99”它们两个字符一组,分别代表红色(FF),绿色(CC),蓝色(99)。所以我们需要先截取字符串,string.sub()可以完成这个要求,然后再转化为数字,我们可以用tonumber()。
我们的转换函数定义如下:
> convertHexToRGB=function (hex)
>>?? local red=string.sub(hex,1,2)
>>?? local green=string.sub(hex,2,3)
>>?? local green=string.sub(hex,3,4)
>>?? local blue=string.sub(hex,5,6)
>>?? red=tonumber(red,16)/255
>>?? green=tonumber(green,16)/255
>>?? blue=tonumber(blue,16)/255
>>?? return red,green,blue
>> end
稍微说明一下两个函数的语法。string.sub()有三个参数,第一个是要被截取的字符串,第二个参数是开始下标,第三个参数是结束下标,返回值就是截取后的子字符串。而tonumber()有两个参数,第一个是要被转换为数字的字符串,第二个是可选参数,如果没写则默认转化一个十进制字符串为十进制数字,如果写了,则按指定的进制(上面代码中的16就是说按16进制来理解第一个参数)来理解第一个参数字符串,并将它转换为十进制数字。返回值就是转换后的数字。
下面看看测试情况:
> print(convertHexToRGB("FFCC99"))
1?????? 0.8???? 0.6
> print(convertHexToRGB("FFFFFF"))
1?????? 1?????? 1
> print(convertHexToRGB("000000"))
0?????? 0?????? 0
5.3??????? 指定多个值
如果要接收有多个返回值的函数,可以使用下面的语法:
var1,var2,var3,var4=somefunction()
somefunction()被调用,它的第一个返回值给var1,第二个返回值给var2,以此类推。如果返回值多于变量,则其它的返回被忽略。
5.4??????? 返回值丢失
多个返回值在一些特殊的情况下会丢失:
如果函数调用所得的多个返回值是另外一个函数的最后一个参数,或者是多指派表达式中的最后一个参数时,所有的返回值将被传入或使用,否则,只有第一个返回值被使用或指定。
先来看函数的例子:
> print("青苹果","梨",convertHexToRGB("FFFFFF"))
青苹果? ????? 1?????? 1?????? 1
可以看到convertHexToRGB()的三个返回值都被print打印出来了,但是如果换个位置,不把这个函数当作print()的最后一个参数,你会发现只会打印一个返回值了。
> print("青苹果",convertHexToRGB("FFFFFF"),"梨")
青苹果?1??????
然后再来看看多指派表达式的情况:
> a,b,c,d="青苹果",convertHexToRGB("FFFFFF")
> print(a,d)
青苹果?1?????? 1?????? 1
a,d四个变量都接收到了值。但如果换一下次序:
> a,d=convertHexToRGB("FFFFFF"),"青苹果"
> print(a,d)
1?????? 青苹果?nil???? nil
我们再一次看到,convertHexToRGB()的三个返回值,只有一个被接收到了。后两个变量都没有值。
5.5??????? WoW中的多个返回值
WoW中的一些API函数返回多个值,如GetRaidRosterInfo()以角色的团队索引(一个数字)作为输入,返回下面的信息:
l?角色名称
l?角色在团队中的级别
l?角色所属子群
l?角色等级
l?角色职业
l?角色职业
l?角色所在区域名
l?角色是否在线
l?角色是否死亡
l?角色是主战士或主辅助
l?角色是否为物品分配人员
函数返回值有多个的时候,我们不一定每一个都需要,这时就带来一个问题,我们如何去获得我们想要的那几个值呢?
1. 使用哑变量
你会看到类似下面的用法:
> _,g=convertHexToRGB("FFFFFF")
第一个变量的名字就叫_,很奇怪的变量名。但是想想也没问题,因为下划线是合法的标识符,所以用它作为变量是没有问题的,而且由于这样的名字通常都不会在程序里正常使用,所以我们用它来接收那些对我们无用的返回值,这就叫哑变量。实际上这样的使用方式并不被认为是一种好的方法。
2. 使用select() 函数
select()函数用来解决这个问题,它允许你指定从第几个返回值开始取值。这个函数可以接受任意数目的参数,其中第一个参数用来决定函数的行为,当第一个参数为“#”时,select()简单返回其余参数的个数,当第一个参数为一个数字时,select()返回其余从这个位置开始直到最后一个的参数:
> print(select("#","a","b","c"))
3
> print(select(1,"c"))
a?????? b?????? c
> print(select(2,"c"))
b?????? c
> print(select(3,"c"))
c
5.6??????? 接受可变数目的参数
类似select()这样的函数,它可以接收可变数目的参数,我们现在就来说说如何实现这样的情况。
5.7??????? 声明变参函数
带有可变参数的函数简称变参函数,它在函数声明中使用三个点(…)来标明,它可以接收任意数目的参数。
在Lua中,三个点(…)可以作为函数声明的最后一个参数,用于声明可选的参数。一旦三个点(…)在函数体中使用,在变参空位中提供的参数就会代替它。比如我们想做一个测试的print()函数,它在一开头会打印“测试:”两个字符,可以这样写:
> test_print=function(...)
>>?? print("测试",...)
>> end
这个函数接受任意数量的参数,然后将其传递给print()函数,并在最前面添加了“测试”两个字符。运行该函数的输出结果如下:
> test_print("苹果","香蕉","梨")
测试??? 苹果??? 香蕉???
当函数运行时,我们可以用以下的形式来使用三个点(…)
--将参数传入另一个函数
print(…)
--将参数指定给有限数量的变量
var1,var3=…
--将参数指定为一个表中的元素
tbl={…}
这样我们就可以写一个函数,使用统一的格式来创建新表:
> newtable=function(...)
>>?? return {...}
>> end
测试一下:
> tbl=newtable("苹果","梨")
> for i=1,#tbl do
>>?? print(tbl[i])
>> end
苹果
香蕉
5.8??????? 结合select()函数使用…
由于可变参数的数量可以是任意的,所以这时就会带来一个难题:我们如何去确定用户在调用这个函数时输入了几个参数?这个问题我们可以使用select()函数来解决:
> test_inputnumber=function(...)
>>?local num=select("#",...)
>>?print("你输入了" .. num .. "个参数")
>> end
这是一个单纯的,仅仅测试你输入了几个参数的函数,我们可以测试一下:
> test_inputnumber(1,4,6)
你输入了6个参数
有了这个信息,我们就可以通过for循环来获得你所你输入的所有参数的值:
> test_select=function(...)
>>?? for i=1,select("#",...) do
>>???? print(i,(select(i,...)))
>>?? end
>> end
在这个for循环中我们使用了两次select(),第一次使用select()获得了参数的个数,第二次使用select()作为print()函数的第二个参数,为了做到每次只输出一个值,我们又在select()的外面又打了一对小括号( 默然说话:你可以试着把 select()外面的那一对小括号去掉,看看会发生什么。)。看看测试结果:
> test_select("星期一","星期二","星期三","星期四","星期五","星期六")
1?????? 星期一
2?????? 星期二
3?????? 星期三
4?????? 星期四
5?????? 星期五
6?????? 星期六
5.9??????? 范型for循环和迭代器
第3章我们学习了for循环,我们可以利用for循环对数组进行遍历。也就是那些下标是数字的表,我们可以很容易的进行循环遍历,但我们要知道表是一个键—值对的形式,它并不止接受数字一种情形,另外,由于是键—值形式,所以我们也认为它们不是一个连续排序的数据,也就是无次序的数列,这种表我们可以叫它为散列表。for循环对这种无次序的数据也提供了处理的方法,这就是范型for循环。
5.10??? 范型for语句的语法
范型for循环的语法与前面的for循环在语法上是有区别的:
for <变量列表> in <表达式> do
<循环体>
end
这个for循环的执行过程是这样的:
第一步:先计算表达式的值,这个表达式必须返回三个值:迭代函数,状态常量,控制变量。
第二步:将状态常量和控制变量传入迭代函数,并调用迭代函数。
第三步:将迭代函数的返回值依次赋值给in前面的变量列表。
第四步:如果迭代函数的第一个返回值为nil,则循环终止
第五步:重复第二步,再次调用迭代函数。
在Lua中已经有了现成的迭代函数,除非有需要,否则我们并不需要去自已编写迭代函数。所以,后面的内容将介绍给大家如何来使用Lua提供的迭代函数。
5.11??? 遍历表的数组部分
ipairs()是Lua提供给我们用于遍历一个表的数组部分的函数。下面是它的一个使用示例:
> tbl={"苹果","梨"}
> for index,value in ipairs(tbl) do
>>?? print(index,value)
>> end
1?????? 苹果
2?????? 香蕉
3??????
相对于,直接使用数值的方式的for循环,我们可以看到这个基于ipairs()函数的循环在书写上显得更简单些。
5.12??? 遍历完整的表
pairs()是一个功能更强大的函数,它可以遍历一个表中的所有元素,无论是数组,还是键—值,还是数组和键—值的混合,它都能遍历出来,看例子:
> tbl={
>>???? "苹果",
>> ????"香蕉",
>>???? "梨",
>>???? width=100,
>>???? height=100,
>> }
> for key,value in pairs(tbl) do
>>?? print(key,value)
>> end
1?????? 苹果
2?????? 香蕉
3??????
height?100
width?? 100
5.13??? 表的清除
通过前面的程序我们可以看出,pairs()函数返回的两个值,第一个是键,第二个是值。所以,我们就可以利用这一点完成对一个表的所有属性进行清除:
> for key,value in pairs(tbl) do
>>?? tbl[key]=nil
>> end
--测试清除效果
> print(tbl[width])
nil
5.14??? 其他的迭代器
Lua里有很多函数都可以产生迭代器函数,string.gmatch()就是其中之一,它可以通过Lua的正则表达式模式匹配产生一个匹配字串的迭代器。在第6章,我们会介绍更多,这里只是举个例子:
> for word in string.gmatch("这是 一个 句子","%S+") do
>> print(word)
>> end
这是
一个
句子
5.15??? 对表的数组排序
表结构内置的table.sort()函数可以使用默认的方式对数字和字符串数据进行排序。如果你希望按照你的想法进行排序,那你可以把你的想法写成一个函数,然后传给table.sort(),它就会按照你的想法来完成排序。这个函数的思路,就是要告诉table.sort()排序时的两个数,哪一个更大些。
5.16??? 定义样例数据
在这里我们要定义一些简单的数据以进行排序:
> guid={}
> table.insert(guid,{
>>???? name="默然的老婆",
>>???? class="牧师",
>>???? level=80,
>> })
> table.insert(guid,{
>>???? name="默然的儿子",
>>???? class="战士",
>>???? level=18,{
>>???? name="真默然",
>>???? class="猎人",
>>???? level=2,
>> })
这是一个表里又装了表的例子,数据显示得比较的复杂了,排序自然也就会变得复杂。因为现在装在guid里的每一个对象都有三个属性,姓名,职业,等级。
5.17??? 默认的排序顺序
在默认的情况下,我们会看到guid表中是按它们的插入顺序进行排序的:
> for key,value in pairs(guid) do
>>?? print(key,value.name)
>> end
1?????? 默然的老婆
2?????? 默然的儿子
3?????? 真默然
5.18??? 创建比较函数
如果我们直接使用table.sort()函数,它会报错,因为它不知道应该怎么对guid里的三个对象进行排序:
> table.sort(guid)
attempt to compare two table values
stack traceback:
??????? [C]: in function 'sort'
??????? stdin:1: in main chunk
??????? [C]: ?
所以,就需要我们来指究竟如何进行排序,也就是写一个比较函数,这个函数有两个参数,这两个参数就类似于我们表中的任意两个对象,我们在这个函数中来指明如何判断对象的大小,那么table.sort()就能知道如何对表进行排序:
> sortLevel=function(a,b)
>>?? return a.level<b.level
>> end
我们这里随意的指定了按对象的等级进行排序,a和b就是guid表中的任意两个对象,我们在sortLevel中对它们的level属性进行了比较,并返回了它们的比较结果,tabl.sort()就可以利用这个比较结果来完成排序,只要象下面这样写:
> table.sort(guid,sortLevel)
然后再次用for循环输出:
> for key,value.name)
>> end
1?????? 真默然
2?????? 默然的儿子
3?????? 默然的老婆
我们看到,已经表中的对象已经改变了顺序。
5.19??? 小结

本章主要介绍了变参函数,范型for循环以及对复杂数组的数据排序的概念。这些概念相对较高级,但是在设计和编写一个新插件时常常遇到。下一章我们将讨论Lua标准库,在这个库中我们将学习如何充分利用Lua提供给我们的帮助来完成我们想要进行的工作。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读