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


第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,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()的三个返回值,只有一个被接收到了。后两个变量都没有值。
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,"a","b","c"))
a       b       c
> print(select(2,"a","b","c"))
b       c
> print(select(3,"a","b","c"))
c
5.6        接受可变数目的参数
类似select()这样的函数,它可以接收可变数目的参数,我们现在就来说说如何实现这样的情况。
5.7        声明变参函数
带有可变参数的函数简称变参函数,它在函数声明中使用三个点(…)来标明,它可以接收任意数目的参数。
在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
苹果
香蕉
5.8        结合select()函数使用…
由于可变参数的数量可以是任意的,所以这时就会带来一个难题:我们如何去确定用户在调用这个函数时输入了几个参数?这个问题我们可以使用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       星期六
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,
>> })
> table.insert(guid,{
>>     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 in pairs(guid) do
>>   print(key,value.name)
>> end
1       真默然
2       默然的儿子
3       默然的老婆
我们看到,已经表中的对象已经改变了顺序。
5.19    小结

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

——摘自 牟勇的笔记

第5章 高级函数和控制结构相关推荐

  1. 一篇文章把你带入到JavaScript中的闭包与高级函数

    在JavaScript中,函数是一等公民.JavaScript是一门面向对象的编程语言,但是同时也有很多函数式编程的特性,如Lambda表达式,闭包,高阶函数等,函数式编程时一种编程范式. funct ...

  2. 《ANSYS 14.0超级学习手册》一第2章 高级应用的基石——APDL

    本节书摘来自异步社区<ANSYS 14.0超级学习手册>一书中的第2章,作者 张建伟 , 白海波 , 李昕, 更多章节内容可以访问云栖社区"异步社区"公众号查看 第2章 ...

  3. python counter函数定义_分享几个自己常用的Python高级函数

    哈喽大家好我是蚂蚁,今天给大家分享几个我自己常用的Python相对高级点的函数,这些函数在特定的场景下能节省大量的代码. 简单列举一下我想要介绍的几个函数: counter:计数器 defaultdi ...

  4. 数据库系统概念总结:第五章 高级SQL

    周末无事水文章,期末备考的总结资料 第五章 高级SQL 5.1 使用程序设计语言访问数据库 5.1.1 JDBC(Java DataBase Connectivity) JDBC标准定义了Java程序 ...

  5. 深入理解Magento – 第六章 – 高级Magento模型

    深入理解Magento 作者:Alan Storm 翻译:Hailong Zhang 第六章 – 高级Magento模型 我们讲过Magento有两种模型,简单模型和EAV(Entity Attrib ...

  6. MOOC —— Python语言基础与应用 by 北京大学 第九章 高级扩展模块

    第九章 高级扩展模块 49.例外处理 50.推导式 51.生成器函数 generator object 52.上机练习:生成器 53.图像处理库 54.Web服务框架 55.网络爬虫 56.数据可视化 ...

  7. 【正点原子Linux连载】第十三章 高级I/O-摘自【正点原子】I.MX6U嵌入式Linux C应用编程指南V1.1

    1)实验平台:正点原子阿尔法Linux开发板 2)平台购买地址:https://item.taobao.com/item.htm?id=603672744434 2)全套实验源码+手册+视频下载地址: ...

  8. 《JavaScript忍者秘籍》(第二版)- 第5章 -精通函数:闭包和作用域

    目录 第5章 精通函数:闭包和作用域 5.1 理解闭包 5.2 使用闭包 5.2.1 封装私有变量 5.2.2 回调函数 5.3 通过执行上下文来跟踪代码 5.4 使用词法环境跟踪变量的作用域 5.4 ...

  9. 第155章 SQL函数 UPPER

    文章目录 第155章 SQL函数 UPPER 大纲 参数 描述 示例 第155章 SQL函数 UPPER 将字符串表达式中的所有小写字母转换为大写字母的大小写转换函数. 大纲 UPPER(expres ...

最新文章

  1. ValueError: cannot convert to ‘int64‘-dtype NumPy array with missing values. Specify an appropriate
  2. 艰难就业季,2020 AI算法岗春招汇总 面经大全来了!!!
  3. ISE14.7安装教程(转)
  4. GLUT及其函数的用法整理
  5. 《物联网框架ServerSuperIO教程》- 23.动态数据接口增加缓存,提高数据输出到OPCServer和(实时)数据库的效率...
  6. 微软 MS Learn 上线 Blazor 入门教程
  7. 【转】建立一个更高级别的查询 API:正确使用Django ORM 的方式
  8. 使用过滤器实现网站访问计数器的功能
  9. 初始化和清理(构造器+重载/重写+this关键字)
  10. 最新版idea2017+kemulator搭建J2ME开发环境
  11. 重装战姬电脑版模拟器怎么玩
  12. 王阳明心学的最高境界
  13. 【AIOT】手表调研
  14. 计算机英语六级时间,计算机一级考试_6月英语六级报名时间
  15. c语言开发桌面应用合适吗,什么编程语言比较适合开发桌面应用程序?
  16. linux bond双活跟主备的区别,“双活中心”比“主备”方式更可靠
  17. mac php dyld: Library not loaded: /usr/local/opt/icu4c/lib/libicui18n.64.dylib 亲测有效
  18. SeetaFace2-master在Windows10 VS2019编译的两种方法
  19. 7-47 说反话-加强版
  20. send 命令 linux,linux的send命令

热门文章

  1. 【ClickHouse SQL 极简教程】使用物化字段投影 PROJECTION 提升性能
  2. 从APP跳转到微信指定联系人聊天页面功能的实现与采坑之旅
  3. 【头歌】——抓取 ARP 命令的包(计算机网络)
  4. Android 系统 ASPK 后缀文件安装
  5. formData传递数组
  6. 2021年化工自动化控制仪表考试题及化工自动化控制仪表最新解析
  7. android 手机号分段_Android EditText输入手机号自带分隔符
  8. [LeetCode]729. 我的日程安排表 I
  9. Python R:数据挖掘、自然语言处理与可视化
  10. 《区块链改变生活》第十八期 大白话聊井通——井通人