lua ~~~~Ⅲ
参考文档:
https://www.runoob.com/lua/lua-data-types.html
https://www.w3cschool.cn/lua/
https://zhuanlan.zhihu.com/p/141875196
本篇学习下面的内容:
- 字符串
- 数组
- 迭代器
- table
- 模块
字符串
string1 = "Lua"
print("\"字符串 1 是\"",string1)
string2 = 'jimmy'
print("字符串 2 是",string2)
string3 = [["Lua 学习"]]
print("字符串 3 是",string3)
字符串转换双引号可以使用 " \ “”。
转义字符 | 意义 | ASCII码值(十进制) |
---|---|---|
\a | 响铃(BEL) | 007 |
\b | 退格(BS) ,将当前位置移到前一列 | 008 |
\f | 换页(FF),将当前位置移到下页开头 | 012 |
\n | 换行(LF) ,将当前位置移到下一行开头 | 010 |
\r | 回车(CR) ,将当前位置移到本行开头 | 013 |
\t | 水平制表(HT) (跳到下一个TAB位置) | 009 |
\v | 垂直制表(VT) | 011 |
\ | 代表一个反斜线字符’’’ | 092 |
’ | 代表一个单引号(撇号)字符 | 039 |
" | 代表一个双引号字符 | 034 |
空字符(NULL) | 000 | |
\ddd | 1到3位八进制数所代表的任意字符 | 三位八进制 |
\xhh | 1到2位十六进制所代表的任意字符 | 二位十六进制 |
字符串操作
序号 | 方法 & 用途 |
---|---|
1 | string.upper(argument): 字符串全部转为大写字母。 |
2 | string.lower(argument): 字符串全部转为小写字母。 |
3 | **string.gsub(mainString,findString,replaceString,num)**在字符串中替换,mainString为要替换的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换) |
4 | string.find (str, substr, [init, [end]]) 在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。 |
5 | **string.reverse(arg)**字符串反转 |
6 | **string.format(…)**返回一个类似printf的格式化字符串 string.format(“the value is:%d”,4) -> the value is:4 |
7 | **string.char(arg) 和 string.byte(arg[,int])**char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。string.char(97,98,99,100) -> abcd |
8 | **string.len(arg)**计算字符串长度。 |
9 | **string.rep(string, n))**返回字符串string的n个拷贝 |
10 | … 链接两个字符串 |
数组
数组,就是相同数据类型的元素按一定顺序排列的集合,可以是一维数组和多维数组。
Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。
-- 一维数组
array = {"Lua", "Tutorial"}
for i= 0, 2 do
print(array[i])
end
array = {}
for i= -2, 2 do
array[i] = i *2
end
for i = -2,2 do
print(array[i])
end
-- 多维数组
-- 初始化数组
array = {}
for i=1,3 do
array[i] = {}
for j=1,3 do
array[i][j] = i*j
end
end
-- 访问数组
for i=1,3 do
for j=1,3 do
print(array[i][j])
end
end
迭代器
迭代器(iterator)是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址
在Lua中迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
泛型 for 迭代器
泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。
for k, v in pairs(t) do
print(k, v)
end
Lua 默认提供的迭代函数 ipairs。
-
初始化,计算in后面表达式的值,表达式应该返回范性for需要的三个值:迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用nil补足,多出部分会被忽略。
-
将状态常量和控制变量作为参数调用迭代函数(注意:对于for结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
-
将迭代函数返回的值赋给变量列表
-
如果返回的第一个值为nil循环结束,否则执行循环体
-
回到第二步再次调用迭代函数
迭代的状态包括被遍历的表(循环过程中不会改变的状态常量)和当前的索引下标(控制变量),ipairs和迭代函数都很简单,我们在Lua中可以这样实现:
function iter (a, i)
i = i + 1
local v = a[i]
if v then
return i, v
end
end
-- 迭代函数、状态常量、控制变量
function ipairs (a)
return iter, a, 0
end
Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。Lua 的迭代器包含以下两种类型:
- 无状态的迭代器
- 多状态的迭代器
无状态的迭代器
无状态的迭代器是指不保留任何状态的迭代器,因此在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。
使用了一个简单的函数来实现迭代器,实现 数字 n 的平方
function square(iteratorMaxCount,currentNumber)
if currentNumber<iteratorMaxCount
then
currentNumber = currentNumber+1
return currentNumber, currentNumber*currentNumber
end
end
for i,n in square,3,0
do
print(i,n)
end
-- 输出
1 1
2 4
3 9
多状态的迭代器
很多情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将所有的状态信息封装到table内,将table作为迭代器的状态常量,因为这种情况下可以将所有的信息存放在table内,所以迭代函数通常不需要第二个参数。
array = {"Lua", "Tutorial"}
function elementIterator (collection)
local index = 0
local count = #collection
-- 闭包函数
return function ()
index = index + 1
if index <= count
then
-- 返回迭代器的当前元素
return collection[index]
end
end
end
for element in elementIterator(array)
do
print(element)
end
Tip:关于上面的迭代器,目前对于状态常量还是不是很理解。这里留个尾巴🐊。以后如果涉及会尽量加深体会。
table
table 是 Lua 的一种数据结构用来帮助我们创建不同的数据类型,如:数字、字典等。
Lua table 使用关联型数组,可以用任意类型的值来作数组的索引,但这个值不能是 nil。
Lua table 是不固定大小的,可以根据自己需要进行扩容。
Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。
-- 初始化表
mytable = {}
-- 指定值
mytable[1]= "Lua"
-- 移除引用
mytable = nil
-- lua 垃圾回收会释放内存
-- 简单的 table
mytable = {}
print("mytable 的类型是 ",type(mytable))
mytable[1]= "Lua"
mytable["wow"] = "修改前"
print("mytable 索引为 1 的元素是 ", mytable[1])
print("mytable 索引为 wow 的元素是 ", mytable["wow"])
-- alternatetable和mytable的是指同一个 table
alternatetable = mytable
print("alternatetable 索引为 1 的元素是 ", alternatetable[1])
print("mytable 索引为 wow 的元素是 ", alternatetable["wow"])
alternatetable["wow"] = "修改后"
print("mytable 索引为 wow 的元素是 ", mytable["wow"])
-- 释放变量
alternatetable = nil
print("alternatetable 是 ", alternatetable)
-- mytable 仍然可以访问
print("mytable 索引为 wow 的元素是 ", mytable["wow"])
mytable = nil
print("mytable 是 ", mytable)
为 table a 并设置元素,然后将 a 赋值给 b,则 a 与 b 都指向同一个内存,就是地址引用。
序号 | 方法 & 用途 |
---|---|
1 | **table.concat (table [, step [, start [, end]]])😗*concat是concatenate(连锁, 连接)的缩写. table.concat()函数列出参数中指定table的数组部分从start位置到end位置的所有元素, 元素间以指定的分隔符(sep)隔开。 |
2 | **table.insert (table, [pos,] value)😗*在table的数组部分指定位置(pos)插入值为value的一个元素. pos参数可选, 默认为数组部分末尾. |
3 | **table.maxn (table)**指定table中所有正数key值中最大的key值. 如果不存在key值为正数的元素, 则返回0。(Lua5.2之后该方法已经不存在了,本文使用了自定义函数实现) |
4 | **table.remove (table [, pos])**返回table数组部分位于pos位置的元素. 其后的元素会被前移. pos参数可选, 默认为table长度, 即从最后一个元素删起。 |
5 | **table.sort (table [, comp])**对给定的table进行升序排序。 |
fruits = {"banana","orange","apple"}
-- ====== 连接
-- 返回 table 连接后的字符串
print("连接后的字符串 ",table.concat(fruits))
-- 指定连接字符
print("连接后的字符串 ",table.concat(fruits,", "))
-- 指定索引来连接 table
print("连接后的字符串 ",table.concat(fruits,", ", 2,3))
-- ====== 插入和移除
-- 在末尾插入
table.insert(fruits,"mango")
print("索引为 4 的元素为 ",fruits[4])
-- 在索引为 2 的键处插入
table.insert(fruits,2,"grapes")
print("索引为 2 的元素为 ",fruits[2])
print("最后一个元素为 ",fruits[5])
table.remove(fruits)
print("移除后最后一个元素为 ",fruits[5])
-- ====== 排序
for k,v in ipairs(fruits) do
print(k,v)
end
table.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do
print(k,v)
end
--====最大值
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
tbl = {[1] = "a", [2] = "b", [3] = "c", [26] = "z"}
print("tbl 长度 ", #tbl)
print("tbl 最大值 ", table_maxn(tbl))
模块
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。(闭包?)
-- 文件名为 module.lua
-- 定义一个名为 module 的模块
module = {}
-- 定义一个常量
module.constant = "这是一个常量"
-- 定义一个函数
function module.func1()
io.write("这是一个公有函数!\n")
end
local function func2()
print("这是一个私有函数!")
end
function module.func3()
func2()
end
return module
Lua提供了一个名为require的函数用来加载模块。要加载一个模块,只需要简单地调用就可以了。
require("<模块名>")
或 require "<模块名>"
-- test_module.php 文件
-- module 模块为上文提到到 module.lua
require("module")
print(module.constant)
module.func3()
-- test_module2.php 文件
-- module 模块为上文提到到 module.lua
-- 别名变量 m
local m = require("module")
print(m.constant)
m.func3()
加载机制
对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 “~/lua/” 路径加入 LUA_PATH 环境变量里:
参考 :http://www.daileinote.com/computer/lua/06
下面内容复制自上面的参考地址
当我们调用require “modname”
**1.**会去检查package.loaded表格是否已经加载过,如果已经加载过直接返回,所以重复加载同一个模块多次只会加载1次
**2.**如果package.loaded表格没有此模块记录,则会到package.path指定的路径中搜索lua文件,如果找到则会调用loadfile加载,会返回一个函数(loader)。如果找不到lua文件,则会去package.cpath指定的路径中搜索动态库so文件,如果找到则会调用package.loadlib加载并寻找luaopen_modname函数(也就是loadfile的结果)。
**3.**不管是在lua文件中找到还是在动态库中找到,现在都已经有了一个loader加载器(函数)
**4.**此时,require调用加载器函数并传入2个参数(大多数模块会忽略参数),返回的任意值会存入package.loaded表格以便下次重复加载时直接返回。如果没返回值,也会当做有返回存入package.loaded(来避免下次重复加载时又白跑一遍2-4步骤)
如果想重复加载模块只需要执行
package.loaded[modname] = nil
那么下次再加载的时候,就会重复跑一遍2-4步骤
添加自定义搜索路径
-- 打印出当前默认搜索的动态库路径 -- ./?.so;/usr/lib64/lua/5.1/?.so;/usr/lib64/lua/5.1/loadall.so print(package.cpath) -- 添加默认搜索路径 package.cpath = '/home/lua/?.so;'..package.cpath -- 打印出当前默认搜索的lua模块路径 -- ./?.lua;/usr/share/lua/5.1/?.lua;/usr/share/lua/5.1/?/init.lua;/usr/lib64/lua/5.1/?.lua;/usr/lib64/lua/5.1/?/init.lua print(package.path) -- 添加默认搜索路径 package.path = '/home/lua/?.lua;'..package.path
▥ ♬ゃō۰• •▥ ♬ゃō۰• •▥ 分割线 ♬ゃō۰• •▥ ♬ゃō۰• •▥ ♬ゃō۰• •
C包
Lua和C是很容易结合的,使用C为Lua写包。
与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。
oadlib函数加载指定的库并且连接到Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为Lua的一个函数,这样我们就可以直接在Lua中调用他。
如果加载动态库或者查找初始化函数时出错,loadlib将返回nil和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))
f() -- 真正打开库
一般情况下我们期望二进制的发布库包含一个与前面代码段相似的stub文件,安装二进制库的时候可以随便放在某个目录,只需要修改stub文件对应二进制库的实际路径即可。
将stub文件所在的目录加入到LUA_PATH,这样设定后就可以使用require函数加载C库了。