第5章 高级函数和控制结构

 

在Lua中,return语句能返回多个值,这些值能让我们更轻松地完成一些工作。例如在WoW中,我们有时必须把十六进制的字符串转化为红绿蓝三色十进制值。
十六进制字符串的典型例子是“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
如果要接收有多个返回值的函数,可以使用下面的语法:
var1,var2,var3,var4=somefunction()
somefunction()被调用,它的第一个返回值给var1,第二个返回值给var2,以此类推。如果返回值多于变量,则其它的返回被忽略。
多个返回值在一些特殊的情况下会丢失:
如果函数调用所得的多个返回值是另外一个函数的最后一个参数,或者是多指派表达式中的最后一个参数时,所有的返回值将被传入或使用,否则,只有第一个返回值被使用或指定。
先来看函数的例子:
> print("青苹果","梨",convertHexToRGB("FFFFFF"))
青苹果  梨      1       1       1
可以看到convertHexToRGB()的三个返回值都被print打印出来了,但是如果换个位置,不把这个函数当作print()的最后一个参数,你会发现只会打印一个返回值了。
> print("青苹果",convertHexToRGB("FFFFFF"),"梨")
青苹果 1       梨
然后再来看看多指派表达式的情况:
> a,b,c,d="青苹果",convertHexToRGB("FFFFFF")
> print(a,b,c,d)
青苹果 1       1       1
a,b,c,d四个变量都接收到了值。但如果换一下次序:
> a,b,c,d=convertHexToRGB("FFFFFF"),"青苹果"
> print(a,b,c,d)
1       青苹果 nil     nil
我们再一次看到,convertHexToRGB()的三个返回值,只有一个被接收到了。后两个变量都没有值。
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,"a","b","c"))
a       b       c
> print(select(2,"a","b","c"))
b       c
> print(select(3,"a","b","c"))
c
类似select()这样的函数,它可以接收可变数目的参数,我们现在就来说说如何实现这样的情况。
带有可变参数的函数简称变参函数,它在函数声明中使用三个点(…)来标明,它可以接收任意数目的参数。
在Lua中,三个点(…)可以作为函数声明的最后一个参数,用于声明可选的参数。一旦三个点(…)在函数体中使用,在变参空位中提供的参数就会代替它。比如我们想做一个测试的print()函数,它在一开头会打印“测试:”两个字符,可以这样写:
> test_print=function(...)
>>   print("测试",...)
>> end
这个函数接受任意数量的参数,然后将其传递给print()函数,并在最前面添加了“测试”两个字符。运行该函数的输出结果如下:
> test_print("苹果","香蕉","梨")
测试    苹果    香蕉    梨
当函数运行时,我们可以用以下的形式来使用三个点(…)
--将参数传入另一个函数
print(…)
--将参数指定给有限数量的变量
var1,var2,var3=…
--将参数指定为一个表中的元素
tbl={…}
这样我们就可以写一个函数,使用统一的格式来创建新表:
> newtable=function(...)
>>   return {...}
>> end
测试一下:
> tbl=newtable("苹果","香蕉","梨")
> for i=1,#tbl do
>>   print(tbl[i])
>> end
苹果
香蕉
由于可变参数的数量可以是任意的,所以这时就会带来一个难题:我们如何去确定用户在调用这个函数时输入了几个参数?这个问题我们可以使用select()函数来解决:
> test_inputnumber=function(...)
>> local num=select("#",...)
>> print("你输入了" .. num .. "个参数")
>> end
这是一个单纯的,仅仅测试你输入了几个参数的函数,我们可以测试一下:
> test_inputnumber(1,2,3,4,5,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       星期六
第3章我们学习了for循环,我们可 以利用for循环对数组进行遍历。也就是那些下标是数字的表,我们可以很容易的进行循环遍历,但我们要知道表是一个键—值对的形式,它并不止接受数字一种 情形,另外,由于是键—值形式,所以我们也认为它们不是一个连续排序的数据,也就是无次序的数列,这种表我们可以叫它为散列表。for循环对这种无次序的 数据也提供了处理的方法,这就是范型for循环。
范型for循环的语法与前面的for循环在语法上是有区别的:
for <变量列表> in <表达式> do
<循环体>
end
这个for循环的执行过程是这样的:
第一步:先计算表达式的值,这个表达式必须返回三个值:迭代函数,状态常量,控制变量。
第二步:将状态常量和控制变量传入迭代函数,并调用迭代函数。
第三步:将迭代函数的返回值依次赋值给in前面的变量列表。
第四步:如果迭代函数的第一个返回值为nil,则循环终止
第五步:重复第二步,再次调用迭代函数。
在Lua中已经有了现成的迭代函数,除非有需要,否则我们并不需要去自已编写迭代函数。所以,后面的内容将介绍给大家如何来使用Lua提供的迭代函数。
ipairs()是Lua提供给我们用于遍历一个表的数组部分的函数。下面是它的一个使用示例:
> tbl={"苹果","香蕉","梨"}
> for index,value in ipairs(tbl) do
>>   print(index,value)
>> end
1       苹果
2       香蕉
3       梨
相对于,直接使用数值的方式的for循环,我们可以看到这个基于ipairs()函数的循环在书写上显得更简单些。
pairs()是一个功能更强大的函数,它可以遍历一个表中的所有元素,无论是数组,还是键—值,还是数组和键—值的混合,它都能遍历出来,看例子:
> tbl={
>>     "苹果",
>>     "香蕉",
>>     "梨",
>>     width=100,
>>     height=100,
>> }
> for key,value in pairs(tbl) do
>>   print(key,value)
>> end
1       苹果
2       香蕉
3       梨
height 100
width   100
通过前面的程序我们可以看出,pairs()函数返回的两个值,第一个是键,第二个是值。所以,我们就可以利用这一点完成对一个表的所有属性进行清除:
> for key,value in pairs(tbl) do
>>   tbl[key]=nil
>> end
--测试清除效果
> print(tbl[width])
nil
Lua里有很多函数都可以产生迭代器函数,string.gmatch()就是其中之一,它可以通过Lua的正则表达式模式匹配产生一个匹配字串的迭代器。在第6章,我们会介绍更多,这里只是举个例子:
> for word in string.gmatch("这是 一个 句子","%S+") do
>> print(word)
>> end
这是
一个
句子
表结构内置的 table.sort()函数可以使用默认的方式对数字和字符串数据进行排序。如果你希望按照你的想法进行排序,那你可以把你的想法写成一个函数,然后传 给table.sort(),它就会按照你的想法来完成排序。这个函数的思路,就是要告诉table.sort()排序时的两个数,哪一个更大些。
在这里我们要定义一些简单的数据以进行排序:
> guid={}
> table.insert(guid,{
>>     name="默然的老婆",
>>     class="牧师",
>>     level=80,
>> })
> table.insert(guid,{
>>     name="默然的儿子",
>>     class="战士",
>>     level=18,
>> })
> table.insert(guid,{
>>     name="真默然",
>>     class="猎人",
>>     level=2,
>> })
这是一个表里又装了表的例子,数据显示得比较的复杂了,排序自然也就会变得复杂。因为现在装在guid里的每一个对象都有三个属性,姓名,职业,等级。
在默认的情况下,我们会看到guid表中是按它们的插入顺序进行排序的:
> for key,value in pairs(guid) do
>>   print(key,value.name)
>> end
1       默然的老婆
2       默然的儿子
3       真默然
如果我们直接使用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 in pairs(guid) do
>>   print(key,value.name)
>> end
1       真默然
2       默然的儿子
3       默然的老婆
我们看到,已经表中的对象已经改变了顺序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值