|
楼主 |
发表于 2014-12-4 16:49:37
|
显示全部楼层
第4章 基本语法
本帖最后由 lzkd 于 2014-12-17 10:46 AM 编辑
.
Lua像C和PASCAL几乎支持所有的传统语句:赋值语句、控制结构语句、函数调用等,同时也支持非传统的多变量赋值、局部变量声明。
4.1 赋值语句
赋值是改变一个变量的值和改变表域的最基本的方法。
好了,之前几乎所有累积的疑问,在这里都会有一个解决(多么熟悉的台词呀,是不是有那句"I'll be back."的味道?)。可如果你认为这里是终极大Boss,那你就错了。充其量,这只是一个武装自己的地方。不相信?那接着看就是了。
在本章开头那句话里,我们需要解决的问题如下:
什么是变量?
什么是赋值?
什么是表域?
4.1.1 变量
4.1.1.1 变量的定义
1.3节讲过,变量即在程序运行过程中它的值允许改变的量。小刀也将变量比喻为一个盒子,这个盒子装的,是各种数据。这个比喻,相信大家肯定能理解,关于定义就不多说了。
4.1.1.2 变量名
所有人都应该有一个名字,变量也不例外,每个变量都应该有自己的名字,说一下变量名的要求。这个在前面已经讲了一部分,除去一些特定的字不能用、不要使用下划线加大写字每之外,还有不要用数字做变量名的起始字符,不能使用中文做为变量名。另外,Lua的变量名大小写敏感,意思就是A和a是两个不同的变量。我们来看一段代码。
- A = 1 --让A的值为1
- a = 2 --让a的值为2
- print(A) --显示A的值
- print(a) --显示a的值
复制代码
4.1.1.3 变量命名技巧
简单说一下,变量命名的技巧。很显然,aa这个变量名,过一段时间,作者自己都会忘掉是什么。相比之下,skillsName,SkillsName,skills_name之类的名字,就要好多了。结论是,充分利用大小写和下划线来命名变量是个好方法。
另外一个小技巧,是关于变量性质的。前面说过,MUD中所有的数据,不是字符串,就是数字。但将数据存入变量后,从外观上,是无法判断出哪个变量存的是字符串,哪个变量存的是数字。这个时候,如果对变量加上前缀就可以一目了然看出来了。一般情况下,n_变量名,表示存放的是数字(number);s_变量名,表示存放的是字符串(string);a_变量名,表示该变量是数组(array);t_变量名,表示该变量是临时变量(tmp)。
4.1.2 赋值
赋值本身比较简单,就是将一个值给到一个变量。具体的方法看代码:
在这里做一个比较详细的解释, = 这个符号,在现实中,是等于号,意思是 = 两边是相等的。但在计算机世界中,这个称为“赋值”号。意思是——将等式右边的值赋予等式左边的变量。上面代码,计算机理解为——将"这是一个测试"赋予t_abc变量。
然后,我们跟3.2节 关系运算符 中 == 放在一起比较一样。 == 和现实中的 = 比较接近。但两者意义是完全不同的。诸君可以仔细体会一下。
如果还是有些混乱,我们可以这么来记忆。 = 肯定是单独使用,而 == 一般会和 if 一起使用(具体见下面的章节)
既然已经说到了赋值,顺带讲一下变量在MUSH中的存取。我们看代码。
- t_test1 = "这是一个测试" --测试用字符串
- t_test2 = 20141208 --测试用数字(注意前缀)
- SetVariable ("t_test", "直接输入") --直接将字符串存入mush
- SetVariable ("t_test", 444) --直接将数字存入mush,注意后一条命令,把前一条数据覆盖
- SetVariable ("t_test1", t_test1) --将变量t_test1存入mush中的变量test1
- print(GetVariable("t_test1")) -- 直接输出mush中变量t_test1的内容
- SetVariable ("t_test1", t_test2) --注意,这后面是t_test2
- print(GetVariable("t_test1")) -- 同上一个print()中的内容是一样的,但输出结果不同
复制代码
我们来做一个说明。从这句可以看出,存入变量到MUSH是用这个命令,SetVariable ("t_test1", t_test1),前面的"t_test1"是MUSH中的变量名,后面的是Lua中的变量名。可能有人会不明白,为什么前面一个一定要加"",在这里简单解释一下。在前面的学习中,我们已经知道,Lua中带""的,会被视为字符串;不带""的数字,会被视为数字;而不带""的非数字,都会被视为变量。大家可以做一个测试,输入print(中文测试),会导致错误。原因就是,所有不带""的非数字,都被视为变量,所以Lua就去找——中文测试——这个变量的值了,但这是找不到的,之前讲过Lua不支持中文变量名,所以只能报错了。所以,前面那个一定要带上"",表明这是一个字符串。
从MUSH取值的命令很简单GetVariable("t_test1"),这个t_test1是MUSH中的变量,同样要加上"",理由同上。另外需要注意的是,所有取出的值,都是字符串,所以如果需要计算从MUSH中取出的值,一定要转化(以前低版本4.3左右的时候,是一直强调这一点的,不知道高版本有没有修正,但不管如何,加上总是不错的,至少兼容性要好一些)
最后说一下,为什么建议新人使用MUSH变量,因为MUSH的值,可以在不操作任何Lua代码的情况下,直接在MUSHclient客户端里看见,这一点,实在是方便极了。
4.1.3 表域(可以理解为数组)
Lua的表(table)是极其灵活和强大的,但因为特别灵活、强大的部分,对于一个普通的机器人没有太大帮助。为了让教程更通俗易懂,在本文仅仅将其当成数组来看待(本来table的设定跟数组几乎就一样)
4.1.3.1 什么是数组
3.5节已经讲了一下数组的定义,即是一个特殊的变量,能够同时保存一个或一个以上的值。
如果不明白的话,我们继续想象。之前我们把变量解释为盒子,看一下数组的定义,里面有这么一句——一个特殊的变量——既然是变量,那当然也是盒子了。不知道,大家对集装箱有没有概念,就是那种很大的铁柜子,一般做对外贸易用的比较多,铁柜子打开以后,里面可以放许多纸箱。(我想不需要真的放一张集装箱的图片上来吧?)数组呢,就跟那集装箱差不多,别的盒子里是装东西,它这盒子里装的是盒子,看起来比较厉害的样子,但不变的是,盒子里的盒子,还是装东西用的。
数组在生活中,其实无处不在,比如说出门买菜,买了2斤青菜、2斤米、半斤肉,然后你把这三样,分别拿塑料袋装好(一般店家都提供),然后你拿出一个大一点的环保袋(多低碳,赞一个),把这三样都装进去。OK,这其实就是一个数组。你就环保袋就是数组,里面有三个数组元素,分别是——青菜、米、肉。
讲到这里,诸君都应该明白数组是怎么回事了。但可能还是有人会有疑问,这么麻烦的东西有什么用?如果要存放数据的话,用普通的变量不就好了吗?如果数量比较多一点,完全可以a1、a2、a3……然后一路a下去。
没错,这样想当然也不是不可以,但总是有些情况是需要数组来解决的。不然的话,小刀为什么费这么大劲想把数组概念解释清楚呢?至于什么情况用数组,请看4.1.3.2节。
4.1.3.2 数组和机器人的关系
什么时候用数组呢,小刀来举两个例子。相信诸君都用过领悟机器人,没用过的,至少也领悟过。在领悟之前,是不是要做一个动作——输入cha命令——这么做的目的,是为了将机器人的使用者的原始数据抓入,以便程序进行判断。在这种情况下,如果使用数组,一个类似a_skills的数组就可以搞定了(至于怎么抓的,我们后面会讲到)。
如果使用普通变量,那就麻烦了——s_skills01,s_skills02,s_skills03……这一个一个装,那也就算了,重点来了,每个人的技能数量都不一样吧?哪怕是同门同派,也会因为对北侠的理解不同,所学技能会有所偏差。那,我们放几个变量合适呢?这个问题,还真是……今天天气,哈哈哈。
第二个例子。比如说,要做胡一刀机器人,老胡发任务的时候,我们需要记录,任务地点,盗宝人姓名,盗宝人id,有些无聊人士,还会把这是第几次任务,今天一共完成了多少次任务,放弃了多少次任务,等等一些资料都做进去。
如果使用普通变量,这一个一个的写过来,写过去的,难道不是很麻烦吗?而且一个不小心,还会有错有漏的说。如果放在一个数组里,直接拎包走人,岂不是很爽?4.1.3.1节中不是说,数组就是一个用来装盒子的大盒子嘛。
好了,两个例子讲完,相信诸君已经很清楚数组的好处了,那我们下节就开始学习如果生成数组。在这里,强调一点,数组也不是随意生成的,应该把具有相关性的数据放进一个数组。如之前的skills,胡一刀任务相关数据等。
4.1.3.3 数组的生成
要想生成数组,得先了解数组的结构,来看一段代码。
- require "tprint"
- t_days = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}
- tprint(t_days)
- print("-------------------这是为了看清楚一点的分割线-----------------------")
- t_quests = {["hyd_city"] = "扬州", ["hyd_name"] = "尤金龙", ["hyd_id"] = "suxxacd"}
- tprint(t_quests)
复制代码
运行后,大致是这个样子。
- 1="Sunday"
- 2="Monday"
- 3="Tuesday"
- 4="Wednesday"
- 5="Thursday"
- 6="Friday"
- 7="Saturday"
- -------------------这是为了看清楚一点的分割线-----------------------
- "hyd_city"="扬州"
- "hyd_id"="suxxacd"
- "hyd_name"="尤金龙"
复制代码
先说一下,代码开头那行——require "tprint"——这是MUSHclient自带的一个查看数组的扩展,使用很简单,看一下就会,效果不错。
诸君都知道,每个变量都会有一个名字,这是为了让计算机找的时候不会出错。同样,数组内的每个值,也需要有一个名字,这个术语称为“下标”。
Lua中数组下标有两种(见运行效果)。一种是连续自然数的排列(t_days数组),有点象以前学校里的学号,是吧。另一种是可以自己定义下标(t_quests数组),这样的好处是使用时更灵活、更方便记忆。
但类似t_quests格式也不是完全没有缺点,因为Lua默认的数组下标是从1开始的(这点跟绝大多数的编程语言不同),而且许多内置的操作都是仅针对下标从1开始的数组。当然,解决方案也不是没有,这个请放心。
说一下这两种格式一般在什么情况下使用。类似t_days格式,比较适合用在大批相同格式数据抓取的情况下,可以理解成输入cha抓取skills,呼一下就都抓下来了;或者用在大量数据输入的情况,因为不需要输入下标,方便一些。
类似t_quests格式,一般用在程序中处理一组有类似性质数据上,可以随时添加,随时使用。
类似t_days的数组的生成,直接按代码中所示就可以了,没什么技术含量。说一下类似t_quests格式数组的生成。本来想放出一个例子,但考虑到需要跟函数结合起来,才是比较完整的代码。不然的话,会有破坏结构的味道,所以到函数时再放出吧。诸君请看代码及注释。
- t_quest = {} --使用数组,必须先定义,这就是定义的格式
- t_quest["hyd_city"] = "xxxx" --将城市信息存入数组,xxx的意思是用某种方法得到的数据,具体见后面章节
- t_quest["hyd_name"] = "xxx" --同上
- ----下面是使用的例子
- print(t_quest["hyd_city"] ) --显示出城市名
- print(t_quest.hyd_name) --显示出任务人名,这里用了另外一种方法,两者是等价的,小刀最近看论坛代码,发现这两种都有人用,特此指出
复制代码
4.1.3.4 数组的遍历及注意点
先要说明一点,这里的遍历,是指读取数组内每一个值,而不是北侠论坛机器人版上一直比较神秘的遍历。按北侠机器人守则,这个是不允许讨论了,抱歉了。
数组的遍历,是一个实用性比较强的功能,但因为Lua比较坑爹的table功能,在使用过程中需要比较小心,不然的话会出一些很麻烦的错误,下面会详细说明。
另外,说明一下,本论坛有bug,有时候使用复制功能粘下来的代码,会出现不应该有的字符,会给诸君的测试造成不便。解决方法是手输或者直接复制,选哪个就随意了。
为写好这个教程,最近把一些基本的东西又仔细梳理了一下。关于数组遍历,网上有好多个版本,研究下来,适合北侠的,有3种,其余要不效率低下,要不太麻烦,就不介绍了。来看代码:
- --第一种方案(命名为方案A)
- --key是键值(也就是教程中的下标),下同
- --value是该key的值,下同
- --tbl为数组,下同
- for key, value in ipairs(tbl) do
- --这里是别的代码
- end
- --第二种方案(命名为方案B)
- for key, value in pairs(tbl) do
- --这里是别的代码
- end
- --第三种方案(命名为方案C)
- for i=1, #tbl do
- --这里是别的代码
- end
复制代码
我们来看一下,这三种方案应该在什么情况下使用。
解释一下方案A的作用——该方案的循环必须要求tbl中的key为顺序的,而且必须是从1开始,ipairs只会从1开始按连续的key顺序遍历到key不连续为止。来看代码。
- tbl = { [1] = 1, [2] = 2, [3] = 3, [4] = 4 }
- for key,value in ipairs(tbl) do
- print("循环内取值 ==> "..value)
- end
- print("循环外取值")
- print(value)
复制代码
运行结果是这样的。
- 循环内取值 ==> 1
- 循环内取值 ==> 2
- 循环内取值 ==> 3
- 循环内取值 ==> 4
- 循环外取值
- nil
复制代码
这里有几个需要注意的地方。
首先,是循环内取值的问题,可以看见,运行结果中,最后的print(value)输出的结果是nil(可以理解为没有值),而之前的print(value)都是有值的(另外几个方案也同样)。所以,以后记得要在循环结束时将需要的值取出。
其次,如果数组下标不是从1开始,又或者当中断了,又或者使用了非数字下标,那就会断掉。篇幅所限,诸君可以自己写代码测试。
关于方案C,其实方案C和方案A的使用要求和输出结果是一样的。来看代码。
- tbl = { [1] = 1, [2] = 2, [3] = 3, [4] = 4 }
- for i=1, #tbl do
- print(tbl[i])
- end
复制代码
输出结果略,希望诸君自己试一下。另外,也试一下,如果将下标改为不连续,或者改为非数字,会有什么结果。只有自己测试过了,写代码的时候,才能做到心中有数。
在实际工作中,一般建议使用方案C来代替方案A。一来,两者使用效果基本一样,方案C的书写更简单一些。二来,细心的同学可能会发现,方案A和方案B实在太接近了,两者就差了一个字符。哪怕是为了减少误写的可能,也是选择方案C比较好一些。
好了,现在我们已经解决了下标为数字的数组的遍历问题,就余下一个下标非数字的遍历了。使用方案B,看代码。
- tbl = { [1] = 1, [2] = 2, ["a"] = 3, [4] = 4 }
- for key,value in pairs(tbl) do
- print("循环内取值 ==> "..key.." --- "..value)
- end
- print("循环外取值")
- print(value)
复制代码
运行结果如下:
- 循环内取值 ==> 1 --- 1
- 循环内取值 ==> 2 --- 2
- 循环内取值 ==> 4 --- 4
- 循环内取值 ==> a --- 3
- 循环外取值
- nil
复制代码
在这里给诸君留一个作业,根据上面的代码和运行结果,能分析出什么注意点吗?
4.2 局部变量
来说一下局部变量。1.3节中已经提到了局部变量的概念,只是没有展开,本节我们来进行比较详细的学习。
一直这样认为,想学好一样东西,前提是对这样事物有比较深入的了解。而所谓深入了解,无非是这几样——怎么来的、能解决什么问题、如何组成、怕什么(喜欢什么)。诸君可以看到,整个教程的核心思路就是如此。学习的时候,诸君也抱着这个试看看,效果会好许多。
局部变量,是指在特定过程或函数中可以访问的变量。如在for或者函数中可以定义一个变量,出了这个for或者函数,这个变量就没用了。来看代码
- t_aaaa = 1000
- function tta()
- local t_aaaa
- t_aaaa = 999
- print(t_aaaa)
- end
复制代码
.
直接运行print(t_aaaa)的话,输出1000,而运行tta()函数的话,输出999。从这个例子可以看出一些两者的区别。
说一下使用局部变量的好处:
1、可以加快程序运行速度。简单解释一下,在整个机器人运行过程中,所有的全局变量Lua会一直记住(放内存里),而局部变量用完就销毁了,更节省资源;
2、不怕同变量名重命名。不同的区域的局部变量是允许同名的,不影响使用;
3、减少变量名记忆量。因为用完就扔,很省心。而全局变量前面说过,需要知道是表示什么意思。
说一下局部变量的特点及不足:
1、局部变量可以与全局变量同名,但是局部变量会屏蔽全局变量。在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。(具体见上面的例子)
2、在程序调试过程中,使用局部变量会造成一定程度的不便。具体表现为,一般出错的情况下,我们会试着将变量结果输出,看看是否为我们所预期的值。这一点上,使用局部变量会有一定的麻烦,因为无法直接print出来。但也不是没办法解决,这个就做为思考题留下,诸君开动一下思路,想想怎么解决。
|
|