第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()以角色的团队索引(一个数字)作为输入,返回下面的信息: @H_403_192@ l 角色名称 @H_403_192@ l 角色在团队中的级别 @H_403_192@ l 角色所属子群 @H_403_192@ l 角色等级 @H_403_192@ l 角色职业 @H_403_192@ l 角色职业 @H_403_192@ l 角色所在区域名 @H_403_192@ l 角色是否在线 @H_403_192@ l 角色是否死亡 @H_403_192@ l 角色是主战士或主辅助 @H_403_192@ 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="默然的老婆", >> , >> level=80, >> }) > table.insert(guID,{ >> name="默然的儿子", >> , >> level=18,{ >> name="真默然", >> , >> 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提供给我们的帮助来完成我们想要进行的工作。
总结以上是内存溢出为你收集整理的魔兽世界编程宝典读书笔记(5)全部内容,希望文章能够帮你解决魔兽世界编程宝典读书笔记(5)所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)