《lua程序设计第4版》学习笔记——基础部分

语言概念

  1. 大小写区分
  2. 脚本语言
  3. 动态类型语言

基本类型

  • nil
  • boolean
  • number
  • string
  • userdata:用来表示由应用或C语言编写的库所创建的新类型
  • function
  • thread
  • table

导入库

1、去其他地方下载第三方库,然后放在C:\Program Files (x86)\Lua\5.1\clibs路径下

2、加载库

local utf8 = require('lua-utf8')

基础类型和语法

标识符

lua语言中的标识符是由任意字母、数字和下划线组成的,但不能以数字开头

“下划线+大写字母”(比如_VERSION)通常有特殊用途,要避免。
“下划线+小写字母”通常用作哑变量。

注释

-- 单行注释
--[[
    多行注释
]]

--[[
    不过多行注释通常用这种写法,因为这样改成---[[就可以安全释放注释了
--]]

全局变量

无需声明即可使用。如果把nil赋值非全局变量时,lua会回收这个全局变量。

boolean

在lua中,除了false和nil以外所有的值都视为真,包括0和空字符串

lua中的逻辑判断

  • and:如果第一个操作符为false,则返回第一个操作符,否则返回第二个
  • or:如果第一个操作符不为false,则返回第一个操作符,否则返回第二个
  • not:返回boolean类型
  • ~=:不等于
  • 三目表达式:lua中没有三目,但是可以用过组合and和or成三目,x and y or z

note:and的优先级高于or

4 and 5  --> 5
nil and 13 --> nil
0 or 5 --> 0
false or nil --> nil

数值(number)

5.2之前,所有数值都是双精度浮点(最大2^53的整型)格式
5.3开始会区分int和float,但是都是number,只有math.type返回的结果不同

算数运算

  • 加减乘除:只有整型之间的加减乘,得出的结果才是整型,其他的都会返回浮点型(除法一定是浮点)
  • floor(//):向下整除。结果向负无限对齐,-9 // 2 = -5。如果两者都是整型,会返回整型
  • 取模(%):如果两者都是整型,会返回整型
  • 幂运算(^):一定返回浮点

数学库

  • math.random(n):1-n生成一个均匀分布的随机数
  • math.randomseed(n):设置种子
  • math.floor(n):整除,向负无穷取整
  • math.ceil(n):整除,向正无穷取整
  • math.modf(n):整除,向0取整

其他操作

  • 通过 + 0.0 实现整型转换成浮点型
  • 通过 | 0 可以实现浮点型转整型(前提是没有小数部分,不然会报错)
  • 或者用math.tointeget(n)方法

字符串(string)

拼接

"hello" .. "world"  --> "hello world"

求长度

a = "hello"
#a --> 5
string.len(a) -->和上面等价

多行字符串

b = [=====[
hello
world
]=====]

b = [[
hello
world
]]

string转int

tonumber("    -3  ")  --> -3
tonumber("    -3e  ")  --> nil
tonumber("100101", 2) --> 37, 第二个参数表示进制

标准库

-- 截取字符串
s = "hello world"
string.sub(s, 2, -2)  --> "ello worl"

-- 输出ascii
print(string.byte("abc", 1, 2)) --> 97 98

-- 字符串格式化
string.format("x=%d, y=%d", 10, 20) --> x=10, y=20
string.format("x=%04d, y=%.4f", 10, 20) --> x=0010, y=20.0000

-- 字符串查找
string.find("hello world", "wor") --> 7 9

utf8标准库

-- 返回长度
utf8.len("哈哈哈")

-- 获取到某个字符的u8编码
-- ps:offset是为了获取到正确偏移量的,不同字符在u8编码中占的位数不同
s = "a阿迪斯发"
print(utf8.offset(s, 3)) --> 5
print(utf8.codepoint(s, utf8.offset(s, 3))) -->36842
print(utf8.char(36842)) --> 迪

表(table)

lua语言中最主要的数据结构,统一了数组、集合、记录等数据结构的表达。

表是一种动态分配的对象,程序只能操作引用。以及lua语言不会进行隐藏的深拷贝

对于表而言,如果程序中不再有他的引用,就会被GC

索引/数组用法

a = {}
a[0] = 1
a["key"] = "a"
print(a["key2"]) --> nil

类/结构用法

a = {}
a.x = 10
a["x"] = 20 --> 和上面等价
print(a.x) --> 20

表的构造

-- 列表式写法
test = {"11", "22", "33"}
print(test[2]) --> 22

-- 记录式写法
test = {x = 10., y = 20}
print(test.x) --> 10

-- 方括号式
test = {["x"] = "xx", [1] = "yy"}
print(test.x) --> xx
print(test[1]) --> yy

构造时,上面的方法都是可以混用的,记录式的键值和列表式的编号不会冲突,可以看作记录式是单独存放的
如果有冲突的话,列表式优先

test = {"11", "22", "33", [2] = "44", [3] = "55", [4] = "66"}
print(test[2]) --> 22
print(test[4]) --> 66
test = {[2] = "44", [3] = "55", [4] = "66", "11", "22", "33"}
print(test[2]) --> 22
print(test[4]) --> 66

遍历表

for k, v in parts(t) do
end

这个遍历过程是随机的

表相关的标准库

  • table.insert(t, v):在表的最后插入v
  • table.insert(t, pos, v):在表的pos位置插入v,后面的元素全部往后移动
  • table.remove(t):删除表最后一个元素
  • table.remove(t, pos):删除表指定位置的元素,后面的元素往前移动
  • table.move(t, front, end, pos):lua 5.3后才有的,t中front到end中间的元素,移动到pos位置上
  • table.move(t, front, end, pos,t2):lua 5.3后才有的,t中front到end中间的元素,移动到t2表的pos位置上

表相关的tips

  • 将nil赋值给表元素可以将其删除
  • 获取数组的长度用#a(假设数组名为a)

表相关的note

  • 被用作表索引时,任何能够被转换成整型的浮点数都会被转换成整型
  • 构造器的第一个元素的索引值时1而不是0,而且必须从1开始,不能是负数或其他值

函数

lua中的函数也支持了oop的调用方式:o:foo(x)

如果函数定义和调用时参数数量不对也不算错误,会自动补充nil或者舍弃多余的参数

function f(a, b) print(a, b) end
f() --> nil, nill
f(3) --> 3, nil
f(3, 4, 5) --> 3, 4

多重返回、多重赋值

在lua中,函数支持返回多个值

function foo2 () return "a", "b" end

以及,在lua中,函数支持对多个值赋值

x, y = y, x --> 交换x和y的值

多返回值缺失问题

调用多返回的函数时,有时会出现返回值丢失的情况:

1、在表达式中调用函数,会把返回值的个数调整成1

print(foo2()) --> a b
print(foo2(), 1) --> a 1
print(foo2() .. "x") --> ax

2、表构造器接收函数调用返回值

t = {foo2()}  -- t = {"a", "b"}
t = {1, foo2(), foo2()} -- t = {1, "a", "a", "b"}

只有在列表中的最后一个函数调用,才包含所有的返回值,其他位置都值保留1个返回值

3、小括号括起来会变成1个返回值

print((foo2())) --> a

可变长参数

传入的方法为三个点

function add (...) end

遍历方法

1、将传入的参数放在一个表里

for _, v in iparts{...} do end

2、用table.pack(lua 5.2后引入)

local arg = table.pack(...)
for i = 0, arg.n do end

这样的方法可以避免传入的参数中有nil,导致创建的表可能不是有序的问题

note: 这个方法相反的方法是table.unpack,是将列表拆成一组返回值。一般会用于调用c函数中,因为c语言函数参数是固定的

3、select函数
如果第一个参数是数值,那会返回对应位置及其后面所有的值
如果是"#",会返回长度

print(select(1, "a", "b")) --> a b
print(select("#", "a", "b")) --> 2

-- 遍历
for i = 1, select("#", ...) do
    select(i, ...)
end

对于参数较少的情况,这种用法比较好,因为避免了创建一个新的表
如果参数较多的话,建议用第一种,因为这种会多次调用这个函数

正确的尾调用

在lua中,如果函数的最后是返回一个函数调用,不做其他的事的话,会把当前函数回收,然后尾调用结束之后直接返回父函数调用的位置。
这样的结果是,在lua中,一些递归死循环不会出现爆栈问题

function foo2(x) do
    return foo2(x + 1)
    -- return foo2(x + 1) + 1
    -- foo2(x + 1)
end

注意:只有上面这种直接return的才算是尾调用,后面两个注释都不是尾调用

IO

  • io.read: 从输入流中读取字符串,总共四种模式:
    • "a" 读取整个文件
    • "l" 读取下一行(丢弃换行符)
    • "L" 读取下一行(保留换行符)
    • "n" 读取一个数字
    • num 以字符串读取num个字符
  • io.write:写入
  • io.open:打开文件。也是四种模式:只读r,只写w,追加a,二进制b
  • io.close:关闭文件
  • io.input:获得当前输入流(加参数是设置当前输入流)
  • io.output:同上,是控制输出流的
  • io.tmpfile:返回一个操作临时文件的句柄
  • io.flush:将缓冲数据写回文件
  • io.seek:保存当前位置
    • io.seek("end"):获取文件大小
    • io.seek("set", io.seek()):恢复当前位置
  • os.rename:文件重命名
  • os.rfemove:文件删除
  • os.exit:程序终止
  • os.getenv("HOME"):获取环境变量
  • os.execute("mkdir file"):运行系统指令。样例是删除file文件夹
  • io.popen:和execute类似

控制结构

条件

if false then
elseif false then
else
end

循环

for 初始值, 终值, 步长 do
end

while true do
    work()
end

repeat
    work()
until true

tips

安全访问

如果我们要调用一个库中的方法,但是不确定是否有这个方法时,可以用表的方式访问

zip = (((company or {}).director or {}).address or {}).zipcode

这样的话,里面的每个字段都被访问了一次,保证了尽可能少地对表进行访问(如果正常按照逻辑判断的话,company字段要被访问四次)

NaN

NaN代表未定义的值,比如0/0,标准规定任何涉及NaN的比较都应返回假

其他

运算符优先度

^
一元运算符(~ # - not)
* /  // %
+ -
··  (连接)
<< >>  (按位移位)
&  (按位与)
~  (按位异或)
|  (按位或)
< > <= >= ~= ==
and
or

学习后提出的问题

  • lua在不同操作系统下获取到的中文路径编码是否有差别?比如win是gbk编码,linux或者移动端是u8编码
posted @ 2022-06-26 14:59  二律背反GG  阅读(222)  评论(0编辑  收藏  举报