电子说
今天我们继续学习Lua语法基础教程,下篇。
在前面的代码中,我们实现了一个函数,输入变量a、b,函数会自动输出两个数值的和。
但是一般来说,我们的需求远远不止这些,我们可能需要一个如下功能的函数:
执行函数,输入两个值,获取这两个值的和
如果还是按上面几节的内容,我们只会输出这个值,并不能把这个值传递给其他的变量进行后续使用,如何解决这个需求呢?
我们可以使用函数的返回值来实现这个需求,结合上面的需求,我们可以用下面的代码实现:
function add(a,b)
return a+b
end
all = add(1,2)
--这里all的值就是3了
print(all)
这里的return表示返回一个值,并且立刻结束这个函数的运行
同时,和输入值可以有多个一样,返回值也可以有多个
function add(a,b)
return a+b,"ok"
end
all, result = add(1,2)
--这里all的值就是3了
--这里result的值就是string "ok"
print(all,result)
下面问题来了,请设计一个函数p,可以按下面的调用方式来返回出物体的密度,返回值为number类型:
function p(a,b,c,m)
--请补全代码
end
--一个长方体的长宽高分别为a、b、c(单位米)
a = 1
b = 2
c = 3
--这个物体重量为m(单位克)
m = 230
--下面返回密度值
--注:密度计算公式 密度 = 质量 / 体积
result = p(a,b,c,m)
print(result)
你需要使用前面几章的知识,来完成下面的题目
function triangle(a,b,c)
--请补全代码
end
result = triangle(1,2,3)--传入值为三边长度,多改几个测试下
print(result)
你需要使用前面几章的知识,来完成下面的题目
function rectangle(a,b)
--补全代码
end
area,len = rectangle(1,2)
--结果:
--面积area为2
--周长len为6
print(area,len)
数组,使用一个变量名,存储一系列的值
很多语言中都有数组这个概念,在Lua中,我们可以使用table(表)来实现这个功能
在Lua中,table是一个一系列元素的集合,使用大括号进行表示,其中的元素之间以逗号分隔,类似下面的代码:
t = {1,3,8,5,4}
我们可以直接使用元素的下标,来访问、或者对该元素进行赋值操作。
在上面的table变量t中,第一个元素的下标是1,第二个是2,以此类推。
我们可以用变量名+中括号,中括号里加上下标,来访问或更改这个元素,如下面的例子:
t = {1,3,8,5,4}
print(t[1]) --打印1
print(t[3]) --打印8
t[2] = 99 --更改第二个元素的值
print(t[2]) --打印99
t[6] = 2 --凭空新建第六个元素并赋值
print(t[6]) --打印2
print(t[10])
--因为不存在,打印nil
以上就是table最简单的一个例子了,就是当作数组来用(注意,一般语言中的数组基本都为不可变长度,这里的table为可变长度)
下面你需要完成:
--请补全代码
cards =
上一节里,我们将table来表示数组,实际上,table中可以包括任意类型的数据
比如我们可以在table中放置number和string数据,类似下面的代码:
t = {"abc",223,",..a",123123}
我们甚至能在里面放function变量
t = {
function() return 123 end,
function() print("abc") end,
function(a,b) return a+b end,
function() print("hello world") end,
}
t1
t2
t3
t4
这些table访问每个元素的方式仍然是直接用下标,并且也能用下标来进行修改
下面你需要完成:
--请补全代码
funcList = {
}
a,b = 1,2--提供两个数
print("a,b值为",a,b)
print("a和b的乘积:",funcList1)
print("a和b的差:",funcList2)
print("a和相反数:",funcList3)
在前两节,我们的table都只是一些简单的List(列表),每个元素的下标都是自动从1排列的
实际上,Lua中,下标可以直接在声明时进行指定,像下面这样:
t = {6,7,8,9}
--上面和下面的代码等价
t = {
[1] = 6,
[2] = 7,
[3] = 8,
[4] = 9,
}
--甚至你可以跳过某些下标
t = {
[1] = 6,
[3] = 7,
[5] = 8,
[7] = 9,
}
print(t[7])
--输出9
--在声明后赋予元素值也是可以的
t = {}--空的table
t[101] = 10
print(t[101])
--输出10
下面你需要:
--请补全代码
t = {
}
print("下标为1的元素:",t[1],type(t[1]))
print("下标为13的元素:",t[13],type(t[13]))
print("下标为666的元素:",t[666],type(t[666]))
在上一节,我们学习了如何自定义下标,其实在Lua中,下标也可以是字符串,如下面的例子
t = {
["apple"] = 10,
banana = 12,
pear = 6,
}
--使用["下标"] = 值
--和 下标 = 值
--都是正确写法
--当第二种方式有歧义时,应该用第一种方式
--可以用下面两种方式访问:
print(t["apple"])
--输出10
print(t.apple)
--输出10
--当第二种方式有歧义时,应该用第一种方式
可见,在使用string作为下标时,table的灵活性提升了一个数量级。
string作为下标时,也可以动态赋值:
t = {} -- 空table
t["new"] = "新的值"
print(t.new)
--输出 新的值
下面你需要完成:
--请补全代码
t = {
}
print("下标为apple的元素:",t["apple"],type(t["apple"]))
print("下标为banana的元素:",t["banana"],type(t["banana"]))
print("下标为1@1的元素:",t["1@1"],type(t["1@1"]))
下面的代码,将会打印什么?
t = {
apple = {
price = 7.52,
weight = 2.1,
},
banana = {
price = 8.31,
weight = 1.4,
year = '2018'
},
year = '2019'
}
print(
t.price,
t.apple.price,
t.banana.weight,
t.year
)
下面的代码,将会打印什么?
t = {
{
price = 7.52,
weight = 2.1,
},
{
price = 8.31,
weight = 1.4,
year = '2018'
},
year = '2019'
}
print(
t["price"],
t[1].price,
t[2].weight,
t["year"]
)
在前面我们知道了,在table中,可以直接用table名[下标]或table名.string下标来访问元素
实际上,在Lua中,所有的全局变量全部被存放在了一个大table中,这个table名为:_G
我们可以用下面的例子来示范:
n = 123--新建变量
print(n)--输出123
print(_G.n)--输出123
_G.abc = 1--相当于新建全局变量
print(abc)--输出1
_G["def"] = 23--相当于新建全局变量
print(def)--输出23
--甚至你可以像下面这样
_G.print("hello")
_G"print"
现在,你明白为什么说万物基于table了吧?
你需要完成下面的任务:
_G["@#$"] = 123
result = --请补全代码
print("result值为",result)
请新建一个名为t的table,满足以下要求
--请补全代码
t = {
}
print("t[10]可获得number类型数据100:",t[10],type(t[10]))
print("t.num可获得number类型数据12:",t.num,type(t.num))
print("t.abc[3]可获得string类型数据abcd:",t.abc[3],type(t.abc[3]))
print("t.a.b.c可获得number类型数据789:",t.a.b.c,type(t.a.b.c))
table.concat (table [, sep [, i [, j ] ] ])
将元素是string或者number类型的table,每个元素连接起来变成字符串并返回。
可选参数sep,表示连接间隔符,默认为空。
i和j表示元素起始和结束的下标。
下面是例子:
local a = {1, 3, 5, "hello" }
print(table.concat(a))
print(table.concat(a, "|"))
-->打印的结果:
--135hello
--1|3|5|hello
请完成下面的任务:
t = {"a","b","c","d"}
print("连接结果:")
--补全代码
table.insert (table, [pos ,] value)
在(数组型)表 table 的 pos 索引位置插入 value,其它元素向后移动到空的地方。pos 的默认值是表的长度加一,即默认是插在表的最后。
table.remove (table [, pos])
在表 table 中删除索引为 pos(pos 只能是 number 型)的元素,并返回这个被删除的元素,它后面所有元素的索引值都会减一。pos 的默认值是表的长度,即默认是删除表的最后一个元素。
下面是例子:
local a = {1, 8} --a[1] = 1,a[2] = 8
table.insert(a, 1, 3) --在表索引为1处插入3
print(a[1], a[2], a[3])
table.insert(a, 10) --在表的最后插入10
print(a[1], a[2], a[3], a[4])
-->打印的结果:
--3 1 8
--3 1 8 10
local a = { 1, 2, 3, 4}
print(table.remove(a, 1)) --删除速索引为1的元素
print(a[1], a[2], a[3], a[4])
print(table.remove(a)) --删除最后一个元素
print(a[1], a[2], a[3], a[4])
-->打印的结果:
--1
--2 3 4 nil
--4
--2 3 nil nil
请完成下面的任务:
t = {1,2,3,4,5,6,7,8,9}
--补全代码
print("第一个元素应为2:",t[1])
print("第三个元素应为810:",t[3])
在实际功能实现中,经常会遇到需要循环运行的代码,比如从1到100填充table数据,我们可以直接用循环语句来实现
我们首先来学习while
这个循环语法,整体的格式如下:
while 继续循环判断依据 do
执行的代码
end
下面举一个例子,我们计算从1加到100的结果:
local result = 0
local num = 1
while num <= 100 do
result = result + num
num = num + 1
end
print(result)
上面的代码,就是当num≤100时,result不断地加num,并且num每次循环后自己加1
理解了上面的代码,我们来完成下面一个简单的任务吧:
min,max = 114,514 --这个结果应为42009
result = 0--结果存放到这个变量
while 请完善 do
--补全代码
end
print("结果:",result)
for循环在某些程度上,和while循环很相似,但是for循环可以更加简洁地表达中间累积的量
我们首先来学习for
这个循环语法,整体的格式如下:
for 临时变量名=开始值,结束值,步长 do
循环的代码
end
其中,步长可以省略,默认为1
临时变量名可以直接在代码区域使用(但不可更改),每次循环会自动加步长值,并且在到达结束值后停止循环
下面举一个例子,我们计算从1加到100的结果:
local result = 0
for i=1,100 do
result = result + i
end
print(result)
上面的代码,就是当i≤100时,result不断地加i,并且i每次循环后增加1
理解了上面的代码,我们来完成下面一个简单的任务吧:
min,max = 114,514 --这个结果应为17955
result = 0--结果存放到这个变量
for --补全代码
print("结果:",result)
前面我们学习了循环语句,有些时候循环运行到一半,我们不想再继续运行了,怎么办呢?
我们可以在一个循环体中使用break,来立即结束本次循环,继续运行下面的代码
比如像下面这样,计算1-100相加途中,小于100的最大的和:
result = 0
for i=1,100 do
result = result + i
if result > 100 then
result = result - i
break
end
end
print(result)
可以看见,当发现和大于100后,代码立即把result的值还原到了加上当前数字之前的状态,并且调用break语句,立即退出了本次循环
在while中,我们也可以使用break:
result = 0
c = 1
while true do
result = result + c
if result > 100 then
result = result - c
break
end
c = c + 1
end
print(result)
我们在这里直接使用了死循环(因为while的继续运行判断依据始终为true),整体逻辑也和之前for的代码一致,当发现和大于100后,代码立即把result的值还原到了加上当前数字之前的状态,并且调用break语句,立即退出了本次循环
现在你需要完成一项任务:
max = 810 --结果应为806
result = 0
for --请补全代码
print(result)
前面我们学习了循环语句,我们需要完成下面的任务
我们知道,print函数可以打印一行完整的输出
那么,已知变量a,请打印出下面的结果:
(a为大于0的整数,且需要输出a行数据,数据从1开始,每行与上一行的差为2)
1
3
5
7
9
(上面例子为当a为5的情况)
做题区域:
a = 10
--需要用print输出要求的结果
print("输出结果:")
for --请补全代码
我们需要完成下面的任务
那么,已知变量a,请打印出下面的结果:
(a为大于0的整数,且需要输出a行数据,第一行为一个,后面每行多一个)
**
(上面例子为当a为5的情况)
做题区域:
a = 10
--需要用print输出要求的结果
print("输出结果:")
for --请补全代码
我们需要完成下面的任务
那么,已知变量a,请打印出下面的结果:
(a为大于0的整数,且需要输出a行数据,按图示规律输出)
1
12
123
1234
12345
123456
1234567
12345678
123456789
12345678910
1234567891011
(上面例子为当a为11的情况)
做题区域:
a = 20
--需要用print输出要求的结果
print("输出结果:")
for --请补全代码
做题区域:
a = 6
--需要用print输出要求的结果
print("输出结果:")
for --请补全代码
接下来几节会讲解string库的各种接口
string.sub(s, i [, j])
返回字符串 s 中,从索引 i 到索引 j 之间的子字符串。
i 可以为负数,表示倒数第几个字符。
当 j 缺省时,默认为 -1,也就是字符串 s 的最后位置。
当索引 i 在字符串 s 的位置在索引 j 的后面时,将返回一个空字符串。
下面是例子:
print(string.sub("Hello Lua", 4, 7))
print(string.sub("Hello Lua", 2))
print(string.sub("Hello Lua", 2, 1))
print(string.sub("Hello Lua", -3, -1))
-->打印的结果:
lo L
ello Lua
Lua
值得注意的是,我们可以使用冒号来简化语法,像下面这样:
s = "12345"
s1 = string.sub(s, 4, 7)
s2 = s:sub(4, 7)
--两种写法是等价关系
print(s1,s2)
请完成下面的任务:
s = "1919810"
--补全代码
print()
print()
print()
string.rep(s, n)
返回字符串 s 的 n 次拷贝。
示例代码:
print(string.rep("abc", 3))
--输出结果:
--abcabcabc
请完成下面的任务:
打印一行数据,数据内容为810个114514
--补全代码
print()
string.len(s)
接收一个字符串,返回它的长度。
示例代码:
s = "hello lua"
print(string.len(s))
--输出结果:
9
--同时也可以使用简便语法
print(s:len())
请完成下面的任务:
s = --补全代码
print()
string.lower(s)
接收一个字符串 s,返回一个把所有大写字母变成小写字母的字符串。
string.upper(s)
接收一个字符串 s,返回一个把所有小写字母变成大写字母的字符串。
示例代码:
s = "hello lua"
print(string.upper(s))
print(string.lower(s))
--输出结果:
HELLO LUA
hello lua
--同时也可以使用简便语法
print(s:upper())
print(s:lower())
请完成下面的任务:
已知一个变量s,打印出全是大写字母的s字符串
s = "asd8938KJjsidiajdl;(()k)"
print --补全代码
string.format(formatstring, ...)
按照格式化参数formatstring,返回后面...内容的格式化版本。
编写格式化字符串的规则与标准 c 语言中 printf 函数的规则基本相同:
它由常规文本和指示组成,这些指示控制了每个参数应放到格式化结果的什么位置,及如何放入它们。
一个指示由字符%加上一个字母组成,这些字母指定了如何格式化参数,例如d用于十进制数、x用于十六进制数、o用于八进制数、f用于浮点数、s用于字符串等。
示例代码:
print(string.format("%.4f", 3.1415926)) -- 保留4位小数
print(string.format("%d %x %o", 31, 31, 31))-- 十进制数31转换成不同进制
d,m,y = 29,7,2015
print(string.format("%s %02d/%02d/%d", "today is:", d, m, y))
--控制输出2位数字,并在前面补0
-->输出
-- 3.1416
-- 31 1f 37
-- today is: 29/07/2015
请完成下面的任务:
n = 810
print --补全代码
这一节我们来讲解字符串的本质
字符串,是用来存储一串字符的,但是它的本质就是一串数字。如何用一串数字来代表一串字符呢?
在计算机中,每一个符号都对应着一个数字,但是在讲解这个知识之前,我们了解一下补充知识:
在大多数编程语言中,我们使用0x开头来表示这个数字是16进制的。
比如
10等价于0x0a
256等价于0xff
接下来,你需要了解,每一个符号都对应着一个数字,比如:
0对应着0x30、1对应着0x31 a对应着0x61、b对应着0x62 A对应着0x41、B对应着0x42
上面的编码规则,我们称之为ascii码,具体想了解可以打开下面的网址查看:http://ascii.911cha.com/
当然,1字节最大为0xff,即256,只能存下一部分符号,大部分的中文按某些编码,一个中文占用2或3个字节
计算机如何解析这些数据,我们不需要了解,当你知道了上面的知识后,你应该可以理解下面的描述:
字符串"apple"实际上的内容就是下面的一串数字:
0x61,0x70,0x70,0x6c,0x65
同时,lua的字符串中可以保存任何数值,即使是0x00这种不代表任何含义的数,也可以保存
补充:在其他语言中(如C),0x00代表字符串结束,但是在lua中并不是这样。
lua的字符串每字节可以存储任意的一字节数据。
比如下面的描述:
有一串lua字符串中的数据为:
0x01,0x02,0x30,0x00,0x44
实际人能看到的(不可见字符用�代替):
��0�D
当然,你不能说你看不见的数据就不存在,他们都完好无损地在这个字符串中
下面你需要思考一个问题:一串字符串数据如下,它的实际内容是什么(指人能看见的字符串内容,如abcd)?
0x62,0x61,0x6e,0x61,0x6e,0x61
string.char (...)
接收 0 个或更多的整数(整数范围:0~255),返回这些整数所对应的 ASCII 码字符组成的字符串。当参数为空时,默认是一个 0。
如果上一章节有认真学习过了的话,这段话应该是很好理解的。实质上就是把计算机认识的一串数字,变成字符串变量,并且字符串内的数据就是要存的那串数据。
示例代码:
str1 = string.char(0x30,0x31,0x32,0x33)
str2 = string.char(0x01,0x02,0x30,0x03,0x44)
print(str1)
print(str2)
-->输出(不可见字符用�代替)
--0123
--��0�D
请完成下面的任务:
t = {0x79,0x6F,0x75,0x20,0x61,0x72,0x65,0x20,0x72,0x69,0x67,0x68,0x74}
print("真正的字符串内容:")
--补全代码
string.byte(s [, i [, j ] ])
返回字符 s[i]、s[i + 1]、s[i + 2]、······、s[j] 所对应的 ASCII 码。i 的默认值为 1,即第一个字节,j 的默认值为 i 。
这个函数功能刚好和前面的string.char相反,是提取字符串中实际的数值。
示例代码:
str = "12345"
print(string.byte(str,2))
print(str:byte(2))--也可以这样
print(str:byte())--不填默认是1
-->输出(十进制数据)
--50
--50
--49
请完成下面的任务:
s = string.char(1,2,3,4,5,6,7,8,9)
print("s内数据的和是:")
--补全代码
string.find(s, p [, init [, plain] ])
这个函数会在字符串s中,寻找匹配p字符串的数据。如果成功找到,那么会返回p字符串在s字符串中出现的开始位置和结束位置;如果没找到,那么就返回nil。
第三个参数init默认为1,表示从第几个字符开始匹配,当init为负数时,表示从s字符串的倒数第-init个字符处开始匹配。
第四个参数plain默认为false,当其为true时,只会把p看成一个字符串对待。
可能你会奇怪,第四个参数有什么存在的必要吗?p不是本来就应该是个字符串吗? 实际上,lua中的匹配默认意义是正则匹配,同时,这里的正则与其它语言也有些许不同。
由于篇幅有限,本节和下面的几节涉及匹配内容时,均不会考虑正则的使用方法,Lua正则教程将会在最后几节单独详细地列出来。
第四个参数为true时,便不会使用正则功能。
示例代码:
--只会匹配到第一个
print(string.find("abc abc", "ab"))
-- 从索引为2的位置开始匹配字符串:ab
print(string.find("abc abc", "ab", 2))
-- 从索引为5的位置开始匹配字符串:ab
print(string.find("abc abc", "ab", -3))
-->输出
--1 2
--5 6
--5 6
请完成下面的任务:
s = "12awsaslwlaawsllslllswasllalssawwlawslaw"
print("两个awsl的位置分别是:")
--补全代码
string.gsub(s, p, r [, n])
将目标字符串s中所有的子串p替换成字符串r。
可选参数n,表示限制替换次数。
返回值有两个,第一个是被替换后的字符串,第二个是替换了多少次。
特别提示:这个函数的目标字符串s,也是支持正则的
下面是例子:
print(string.gsub("Lua Lua Lua", "Lua", "hello"))
print(string.gsub("Lua Lua Lua", "Lua", "hello", 2)) --指明第四个参数
-->打印的结果:
-- hello hello hello 3
-- hello hello Lua 2
同样的,我们也可以使用冒号来简化语法,像下面这样:
s = "12345"
r = s:gsub("2","b")
print(r)
请完成下面的任务:
s = "asdicagydausckfugdaflgscdabgsdbahhacbshbsd"
print("s变换前的值:",s)
--补全代码
在编写代码时,随着逻辑逐渐复杂,我们的代码量也会变大。虽然有函数可以把一部分代码逻辑封装起来,但是所有代码都放到一个文件里,显然也不是个好办法。
所以我们需要将一些代码放到不同文件中,通过文件来区分这些代码的功能。
比如我们有下面这个函数:
---函数功能:
-- 生成从1-max的table
-- @输入值:table的最大值
-- @返回: table结果
-- @例子: local list = getNumberList(10)
function getNumberList(max)
local t = {}
for i=1,max do
table.insert(t,i)
end
return t
end
我们新建一个文件叫tools.lua,把这个函数放进去,现在,整个文件如下面这样:
tools.lua
---函数功能:
-- 生成从1-max的table
-- @输入值:table的最大值
-- @返回: table结果
-- @例子: local list = getNumberList(10)
local function getNumberList(max)
local t = {}
for i=1,max do
table.insert(t,i)
end
return t
end
--手动返回一个table,包含了上面的函数
return {
getNumberList = getNumberList,
}
现在,我们封装的这个函数就能在其他文件里被调用了,具体代码如下:
--引用tools.lua文件,并加载
local tool = require("tools")
local list = tool.getNumberList(12)
当调用了require接口后,Lua虚拟机会自动加载你调用的文件,执行文件的内容,然后返回你文件里return的结果。
为了更好地理解这段话,我们可以看下面两个文件,其中run.lua是被运行的那个入口文件
test.lua
--以便一会儿返回使用的table
local temp = {}
--把全局变量a更改了
a = 1
--local变量无法被外部调用
--但是可以在文件内被调用
local b = 2
--文件在被require的时候,会被执行
--把全局变量c更改了
c = a + b
--使函数在table里
function temp.addB()
--文件内部可以调用变量b
b = b + 1
return b
end
--返回table
return temp
run.lua
local test = require("test")
print(a)--输出1
print(b)--输出nil,因为b是local变量
print(c)--输出3
print(test.addB())--输出3
print(test.addB())--输出4
print(test.addB())--输出5
同时,每个文件最多只会被require一次,如果有多个require,只有第一次会执行。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !