Lua学习笔记(复习)
Lua学习笔记(复习)
文章目录
SF、速记内容
Lua访问全局变量
__G.变量名
;
Top1: 注释符号
-- 单行注释
--[[
多行注释;
--]]
多行注释(推荐使用方式):这样可以避免遇到table[table[idx]]时就将多行注释结束了
。
--[=[
注释内容
注释内容
注释内容
]=]
Top2: 控制台运行方式
--1.直接控制台打印输出
print("Hello Lua!")
--2.使用lua命令加载lua文件输出
$ lua hello.lua
--3.Shell脚本
-- -脚本内容(hello.lua)
#!/usr/local/bin/lua
print("Hello Lua !");
-- -Lua解释器的位置,usr/local/bin目录下
-- -打印输出
./hello.lua
Top3: +和… 符号的注意!
-- 运行时,Lua会自动在string和numbers之间自动进行类型转换,
-- 当一个字符串使用算术操作符时, string 就会被转成数字。
print("10"+1) --11
print("10+1") --10+1
print("hello"+1) --报错,无法转行"hello"
-- 反过来,当 Lua 期望一个 string 而碰到数字时,会将数字转成 string。
print(10 .. 20) --1020
-- ..在Lua中是字符串连接符,当在一个数字后面写 .. 时,必须加上空格以防止被解释错。
-- ..才是字符串连接,而`+`则是计算(如果运算时,字符串无法被转化成数字则会报错)。
Top4: 数组和表的区别?
Top5: pairs 和 ipairs 的区别?
- pairs 遍历全部非空的
- ipairs 从第一个开始遍历 遇到空的就停止遍历
Top6: 符号. 和 符号: 的区别?
-
.
用.需要传递对象 静态方法用.
-
:
用:不需要传递对象 非静态方法用:
1F、语法规则
标识符
- 不建议使用
下划线加大写字母的标示符
,因为Lua的保留字也是这样的; - 除此之外,和C语言的标识符规则一样;
关键字
-
流程控制
- 条件控制:
if
,else
,elseif
; - 循环控制:
for
,while
,repeat
和until
; - 节点控制:
do
,end
,break
,then
;
- 条件控制:
-
运算符
- 逻辑运算符:
and
,or
,not
; - 布尔运算符:
true
,false
;
- 逻辑运算符:
-
其他类型
- 定义方法:
function
,return
; - 局部变量:
local
; - 无效数据:
nil
;
- 定义方法:
- 一般规定:以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。
运算符
类型 | 运算符描述 |
---|---|
基本 |
+ ,- ,* ,/ ; |
其他 |
% (取余),^ (乘幂),- (负号); |
. |
? |
.. |
用于字符串连接 |
... |
? |
: |
? |
# |
一元运算符,返回字符串或表的长度;也可以使用 select('#',...) ; |
条件 |
== ,~= (不等于),> ,< ,<= ,>= ; |
逻辑 |
and (与),or (或),not (非); |
运输优先级
--除了 ^ 和 .. 外,所有的二元运算符都是左连接的;
--运算符支持括号优先级;
^
not - (unary)
* /
+ -
..
< > <= >= ~= ==
and
or
乘幂示例代码
--乘幂:多个N相乘的结果
n = 3
print("a^4 = ",n^4) --81
全局变量
在默认情况下,变量总是认为是全局的。
全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。
print(b) --nil
b=10
print(b) --10
删除一个全局变量,只需要将变量赋值为nil
b=nil
print(b) --nil
数据类型
Lua是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。
-
8种数据类型:
- nil,无效值(条件判断中为false)
- boolean,布尔类型
- number,双精度类型的实浮点数
- string,单引号|双引号 表示;
- function,函数类型(C|Lua的函数)
- thread,独立线路,用于执行协同
- table,表结构,本质是关联数组,数组索引可以是数字或字符串,通过构造表达式{}创建
- userdata,存储的C数据结构的变量
-
使用
type(val)
可以测试数据类型;
print(type(print)) --function
print(type(type)) --function
print(type(true)) --boolean
print(type(nil)) --nil
print(type("Hello!")) --string
print(type(type(X))) --string,(见t1.无效`nil`章节)
t1. 无效:nil
- 没有赋值的值为nil,把一个变量nil则会着释放这个变量;
- 对于全局变量和 table,nil 则是"删除"的作用,给全局变量或者 table 表里的变量赋一个 nil,便是把它们删掉;
-- 初始化表,并遍历
tab1 = {k1 = "val1",k2 = "val2","val3"}
for k, v in pairs(tab1) do
print(k.." - "..v)
end
-- 将表中的某个k赋值为nil
tab1.k1 = nil
for k, v in pairs(tab1) do
print(k.." - "..v)
end
- nil 作比较时,应该加上双引号,例如:
type(X)=="nil"
。(见下代码) -
type(X)==nil
结果为 false ,原因是type(type(X))==string
。(见下代码)
type(X) --nil
type(X)==nil --false
type(X)=="nil" --true
t2. 布尔:boolean
- Lua 把 false 和 nil 看作是"假",其他的都为"真"(包括0,所以
0==true
):
print(type(true)) --boolean
print(type(false)) --boolean
print(type(nil)) --nil
if false or nil then
print("至少有一个是true");
else --[run to here!]
print("false 和 nil都为false!");
end
t3. 数字:number
- Lua 默认只有一种 number 类型 -->
double(双精度)
类型。(默认类型可以修改 luaconf.h 里的定义)
print(type(2))
print(type(2.2))
print(type(0.2))
print(type(2e+1))
print(type(0.2e-1))
print(type(7.8263692594256e-06))
-- 以上所有代码的执行结果均为number;
t4. 字符串:string
1. 字符串的3种创建方式
字符串的声明方式:
单引号' '
,双引号" "
,双中括号[[ ]]
--双引号声明
string s1 = "Hello Lua !"
--单引号声明
string s2 = 'Hello Lua !'
--双中括号声明
html = [[
<html>
<head></head>
<body>
<a href="http://www.runoob.com/">菜鸟教程</a>
</body>
</html>
]]
print(html) --html内容的打印
2. 字符串的转义字符
\a 响铃
\b 退格
\f 换页
\n 换行
\r 回城
\t 水平制表
\v 垂直制表
\\ /符
\' '符
\" "符
\0 Null空字符
\ddd 1~3位OCT
\xhh 1~2位HEX
3. 字符串操作方法
- 1.字符串大小写转换
--(1)【字符串全部转为大写字母】
string.upper(argument)
--(2)【字符串全部转为小写字母】
string.lower(argument)
- 2.字符串替换、查找、反转
--(3)【字符串替换】
string.gsub(mainString,findString,replaceString,num)
--mainString: 操作字符串
--findString: 被替换字符
--replaceString: 要替换的字符
--替换次数(忽略为全部替换)
--(4)【查找字符串】
string.find(str,substr,[init,[end]])
--str: 操作字符串
--substr: 要查找的字符串
--[init,[end]]: 查询的索引范围init~end
--返回值: 返回具体位置,不存在返回nil;
--(5)【字符串反转(将字符串反过来)】
string.reverse(arg)
- 3.字符串的格式化输出
--(6)【类似C语言printf()的格式化输出,返回一个格式化字符串】
string.format(...)
--Example
string.format("the value is : %d",4) -- the value is : 4
- 4.字符串和数字的相互转换
--(7)数字转字符串、字符串转数字
string.char(arg)和string.byte(arg[,int])
--Example
string.char(97,98,99,100) -- abcd
string.byte("ABCD",4) -- 68
string.byte("ABCD") -- 65
- 5.其他,字符串的长度计算、重复拷贝、拼接等
--(8)【计算字符串的长度】
string.len(arg)
--除此,还有#和select('#',arg)两个方式;
--(9)【返回字符串string的n个(次)拷贝】
string.rep(string,n)
--Example
string.rep("abcd",2) --abcdabcd
--(10)【使用两个..符号,连接两个字符串】(略)
- 6.字符串匹配
--(11)迭代器函数,每一次调用这个函数,持续在str中寻找符合pattern规则的子字符串;没有则返回nil
string.gmatch(str,pattern)
--Example
for w in string.gmatch("Hello Lua user","%a+") do
print(w) --Hello、Lua、user这个几个单词;
end
--(12)
string.mathc(str,pattern,init)
- 更多
- 字符串用于算术(
+、-、*、/
),Lua则会进行类型转换;
print("2" + 6) --8.0
print("2" + "6") --8.0
print("2 + 6") --2+6,纯字符串是原样打印的;
print("-2e2" * "6") --1200.0
print("error" + 1) --报错
- 连接字符串的符号是
..
,为两个点(连接字符串不是+号!!
),可以连接字符和任意类型(理论上来说);
print("a" .. 'b') --ab
print(157 .. 428) --157428
- 可以使用
#计算字符串的长度
print(#"Hello World") --计算字符串的长度
len = "Hello World"
print(#len) --计算字符串变量的长度
t5. 表:table
- 关于table表:
-
个人理解:表就是关联数组和索引数组的集合,类似结构体
)。 - 菜鸟描述:Lua中的表(table)其实是一个"关联数组"(
associative arrays
),索引下标可以是数组或字符串 - Lua中表的索引一般是从 1 开始的,这个是重点。
- 表可以自增。没有的值的地方默认是nil值。
-
--【创建表】
-- 创建空table
local tab1 = {}
-- 直接初始化表
local tab2 = {"hello","aloha","你好"}
--【表的索引】
--一个table的lua脚本文件
a = {}
a["key"] = "values"
key = 10
a[key] = 22
a[key] == a[key] + 11
for k v in pairs(a) do
print(k.. " : " .. v)
end
--【表的自增,没数据时默认为nil值】
a3 = {}
for i = 1, 10 do
a3[i] = i
end
a3["key"] = "val"
print(a3["key"]) --val
print(a3["none"]) --nil
- table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表;
- table其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串;
- 在 Lua 里表的默认初始索引一般以 1 开始,表也是如此;
- table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table 都是 nil;
--创建一个空table
local tab = {}
--直接初始表
local tab = {"apple", "pear", "orange", "grape"};
--表可以使用关联
tab["key"] = "value";
--表可以使用索引
tabl[10] = 22;
- 表的索引方式:(位置:菜鸟教程-Lua教程-变量模块-索引)
对table的索引使用方括号[],Lua也提供了.
操作。
--【说明】
t[i]
t.i --当索引为字符串类型时的一种简化写法
gettable_event(t,i) -- 采用索引访问本质上是一个类似这样的函数调用
-- 个人理解,就是将表名和关联下标名传递进一个函数,然后进行处理;
--【示例】
site={}
site["key"] = "www.w3scoll.com"
print(site["key"]) -- www.w3scoll.com
print(site.key) -- www.w3scoll.com 结果和上个一样;
t6. 函数:function
- 在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里;
- 个人总结,
就是说函数可以跟变量一样使用,类似C#中的委托(面向对象的,应该是可以操作类实例函数原因)和C++中的函数指针(过程)
;
--方法作为类型变量(一个简单方法递归)
function fun(n)
if n==0 then
return 1
else
return n*fun(n-1)
end
end
--调用方法
print(f(5))
--把函数赋值变量,通过变量使用函数(重点)
fv = fun; print(fv(5))
- 匿名函数
将方法作为类型参数,然后在函数中使用;
--定一个普通方法
function fun(tab,anonymous_f)
for k,v in pairs(tab) do
print(anonymous_f(k,v))
end
end
--定义一个表;
tab = {key1="val1",key2="val2"};
--参数:(表,匿名参数)
--通过调用匿名方法,在匿名方法中,返回一个整理好的字符串;
fun(tab,
function(key,val)
return key.."="..val;
end
);
t7. 线程:thread
这里只作说明;
在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。
t8. 自定义类型:userdata
这里只作说明;
userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
2F、操作方式
1、变量定义
- Lua 变量有三种类型:
全局变量
、局部变量
、表中的域
(table.key的这种形式)。 - Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非
用 local 显式声明为局部变量
。 - 局部变量的作用域为从声明位置开始到所在语句块结束。
- 变量的默认值均为 nil。
- 重点,应该尽可能的使用局部变量,有两个好处:
- 避免命名冲突;
- 访问局部变量的速度比全局变量更快。
- I、Lua可以对多个变量的同时赋值:(
变量列表和值列表的各个元素用逗号分开
)
a,b = 10, 2*x <--> a=10;b=2*x
- II、遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作(
好好理解这句话的重点
),所以我们可以这样进行交换变量的值
。(就是说Lua会先计算右边所有的值,计算完之后,再对右边的所有的变量进行赋值,并不是以单个计算就赋值的!!)
-- 实际测试的结果值;
x = 10
y = 20
x,y=y,x
print("x: " .. x); --x: 20
print("y: " .. y); --y: 10
- III、多个变量赋值的不一致问题:
- 变量个数 > 值的个数,按变量个数补足nil;
- 变量个数 < 值的个数,多余的值会被忽略;
a, b, c = 0 --> 并不会a之后的bc都赋值为0的,这个要注意;
print(a,b,c) --> 0 nil nil
a, b, c = 0, 0, 0 --> 要给三个变量都赋值0,这个才是正确的做法;
- IIII、f()返回两个值,第一个赋给a,第二个赋给b;
a,b = f();
2、循环体
break
关键字可以退出(当前层的)循环。
第一种:while循环
while(true)
do --做什么
--循环体
end --循环内容的N次结束
第二种:repeat…until()循环
--【格式】
repeat
--执行内容
until(条件)
--【示例】
a = 10
repeat
print("a=>",a)
a=a+1 --Lua好像不支持 += 符号;
until(a>15)
第三种:for循环
- lua中for语句有两种:
- [
数值for循环
]; - [
泛型for循环
];
- [
- [数值for循环]
-- 【语法结构】
-- for 初始值,判断,迭代
for exp1,exp2,expe do
print("方法体执行内容");
end
-- 【语法注释】
-- exp1:是初始化条件,例如 i=1;
-- exp2:是限制条件,只执行一次;
-- exp3:是递增递减条件,属于可选,默认是+1,可以写成-1;
-- 【演示代码】
-- Example1:
for i=1,f(x) do --不用管这个代码为何不能执行,反正就是告诉你第二项只会执行一次;
print(i)
end
-- Example2:
for i=10,1,-1 do
print(i)
end
- 重点:for的三个表达式,在循环开始之前一次性求值,以后不再进行求值。比如上面
- 个人理解:
开始循环之前
,先计算一次表达式的结果值,之后就执行循环计算,不再计算。 - 个人理解:
反正总得来说,第二个表达式在for循环中无论如何,都只会执行一次
。
#!/usr/local/bin/lua
function f(x)
print("function")
return x*2
end
for i=1,f(5) do print(i) end
--打印结果: 1 2 3 4 5 6 7 8 9 10
- [泛型for循环]
泛型for循环通过一个迭代器函数来遍历所有值,类似java中的foreach语句。
--【语法结构】
a = {"one","two","three"}
for i, v in ipairs(a) do
print(i,v)
end
--【结构注释】
--i,是数组索引值,v时对应索引的数组元素值。
--ipairs时Lua提供的一个迭代器函数,用了迭代数组;
--【代码示例】
days = {"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"}
for i,v in ipairs(days) do print(i..","..v) end
--【代码结果】
--1,Sunday
--2,Monday
--3,Tuesday
--4,Wednesday
--5,Thursday
--6,Friday
--7,Saturday
重点:Pairs与iPairs的区别
在lua中,pairs与ipairs两个迭代器的用法相近,不同在于:
- 说法1:
- pairs可以遍历表中的所有key,并且除了迭代器本身以及遍历表本身还可以返回 nil。
- ipairs则不能返回nil,只能返回数字0,如果遇到nil则退出。它只能遍历到表中出现的第一个不是整数的key。
- 说法2:
- pairs遍历表中全部key,value
- ipairs从下标为1开始遍历,然后下标累加1,如果某个下标元素不存在就终止遍历。这就导致如果下标不连续或者不是从1开始的表就会中断或者遍历不到元素。
- 说法3:
- 首先,ipairs 这个迭代器只能遍历所有数组下标的值,这是前提,也是和 pairs 的最根本区别,也就是说如果 ipairs 在迭代过程中是会直接跳过所有手动设定key值的变量。
- 特别注意一点,和其他多数语言不同的地方是,迭代的下标是从1开始的。
tab = {1,2, a = nil,nil,"d"}
-- pairs遇到nil会跳过nil继续循环
for i, v in pairs(tab) do print(i,v) end
-- ipairs遇到nil会跳出循环
for i, v in ipairs(tab) do print(i,v) end
- 说法4:
for i=1,3 do
i = 10
print("One Time:"..i)
end
- 说法5:
- pairs 能迭代所有键值对。
- ipairs 可以想象成 int+pairs,只会迭代键为数字的键值对。
3、条件体
Lua没有Switch语句;
if(0) -- [特别注意: 0为ture ! ! ! ! ],Lua认为false和nil为假,true和非nil为真;
then -- 条件结构可以嵌套执行- (Ex:略);
--(TRUE执行体)
elseif
--(ELSE IF 执行体)
else
--(FALSE执行体)
end
4、方法体
1. 多参返回
Lua中的return可以返回多个参数,用"
,
"号隔开;
--【定义方法体/函数】(local定义了局部函数)
local function Fun(args)
print(args) --方法体内容;
return 100,55,66 --1.返回值不需要写,直接返回;2. reutrn可以返回多个参数;
end --参数接受,可以使用多个变量接受;
--【调用函数】
print("RetValue: ",Fun("你好!"));
v1,v2,v3 = Fun("hello")
print(v1)
2. 函数作为参数
将函数作为参数传递调用的示例,
将函数作为参数,传递后在另外的函数中必须调用才可以使用
。
--将匿名函数赋值给变量
myprint = function(param)
print("this is print fun --> ",param," ##")
end
--
function add(n1,n2,funprint)
result = n1+n2;
funprint(result) --调用传递过来的函数
funprint(result)
end
myprint(10) -- 设置myprint变量函数的参数
add(2,5,myprint); -- myprint函数作为参数传递;
--【显示结果】
-- this is print fun --> 10 ##
-- this is print fun --> 7 ##
-- this is print fun --> 7 ##
3. 多参返回的示例
string.find("字符串","子串")
,返回开始和结束的位置;
--【示例1】
s,e = string.find("www.runoob.com","runoob")
print(s,e) -- 5 10,如果找不到则两个都返回nil;
--【示例2】
function maximum(a)
local mi = 1
local m = a[mi]
for i , val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m,mi
end
--传递一个表过去,这里可以打印所有的返回值;
print(maximum({8,10,23,12,5})) -- 打印结果: 23 3
4. 函数的可变参数 …
可变参数的示例。
--【格式】
function add(...)
local s = 0
for i, v in ipairs{...} do -- "..."表示一个由所有变长参数构成的数组
s = s+v
end
return s
end
print(add(3,4,5,6,7)) -- 25
--【示例:将...赋值给一个变量】
function average(...)
result = 0
local arg = {...}
for i , v ipairs(arg) do
result = result+v
end
print("总共传入了 " .. #arg .. "个数.") -- 6
return result/#arg
end
print("平均值为: " average(10,5,3,4,5,6)) -- 5.5
--【示例:使用select("#",...)来统计可变参数的个数】(只需将#arg更换即可)
print("总共传入了 " .. select("#",...) .. "个数.")
return result/ select("#",...)
--【示例: 固定参数必须放在变长参数之前】 (固定参数在最左边)
function fwrite(fmt, ...)
return io.write(string.format(fmt, ...))
end
fwrite("runoob\n") --无可变参数
fwrite("%d%d\n",1,2) --携带可变参数的情况下
-- 输出结果: runoob 12
5. select()处理 nil参数
包含nil的可变参数需要用
select('#',...),select(n,...)
(重点看下面的1和2,这里不重要)来处理
-
通常在遍历变长参数的时候只需要使用 {…},然而变长参数可能会包含一些 nil,那么就可以用 select 函数来访问变长参数了:
select('#', …)
或者select(n, …)
-
select('#', ...)
,返回可变参数的长度; -
select(n, ...)
,访问 n ~select('#', ...)
的参数;
-
-
调用select时,必须传入一个固定实参selector(选择开关)和一系列变长参数。
- 如果selector为数字n,那么select返回它的第n个可变实参,否则只能为字符串"#",这样select会返回变长参数的总数
do
function fun(...)
for i=1,select('#', ...) do
local arg = select(i, ...); --获取参数的值
print("arg: ",arg); --打印参数的值
end
end
fun(1,2,3,4);
end
6. 扩展领域
--[示例]
function Fun2(arg)
{
--支持委托形式
print(Fun2("123"))
}
--[可变]
function Fun3(...)
-- print("123".."321")
tab = {...}
print(#tab) --数量;长度;
print(tab[1]) --从1开始;
end
—— 符合类型
迭代器
- 废话直接拷贝过来:
- 迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址
- 在Lua中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
划重点:
泛型For迭代器
-- for迭代器提供了集合的key/val对;
-- 重点pairs:可便利全部,会跳过nil值;
for k, v in pairs(t) do
print(k, v)
end
--重点ipairs:遇到不连续的下标或值为nil就会停止;
arr = {"lua","Tutorial"}
for key, value in ipairs(arr)
do
print(key,value)
end
数组
- 数组声明
- 数组的索引从1开始,在Lua中;但可以设置为从负数或0开始;
- 这里的是从0开始计算;
- 默认的步进值应该是+1;
arr = {"lua","Tutorial"}
for i=0,2 do
print(arr[i])
end
--[从-2开始的数组的索引]
arr = {}
for i=-2,2 do
arr[i] = i*2;
end
for i = -2,2 do
print(arr[i])
end
--[二维数组]
arr = {}
for i=1,3 do
arr[i] = {}
for j=1,3 do
arr[i][j] = i*j
end
end
for i=1,3 do
for j=1,3 do
print(arr[i][j])
end
end
--[二维数组的索引示例]
arr = {}
MR = 3 MC = 3
for row=1,MR do
for col=1,MC do
arr[row*MC + col] = row*col
print("index:"..(row*MC + col)..", Val: "..row*col)
end
end
--[输出结果]
--[[
index:4, Val: 1
index:5, Val: 2
index:6, Val: 3
index:7, Val: 2
index:8, Val: 4
index:9, Val: 6
index:10, Val: 3
index:11, Val: 6
index:12, Val: 9
]]
上一篇: poj1001Exponentiation高精度幂运算
下一篇: Data Structures