《Lua程序设计》1-8章读书笔记
为什么使用lua:
通常组件表示了一个更具体更底层的概念,在程序开发过程中很少会修改它们,并且它们会占用最终程序的大部分cpu时间。lua则可以连接这些组件,给出一个应用程序最终的样子,而这部分内容可能会在一个产品的开发周期中被反复修改
程序块:
一连串的语句或命令
交互模式下,每行输入的内容自身形成一个程序块,在这种情况下,local的作用域可能与预期的不同
dofile("xxx.lua") 在交互模式下加载xxx.lua文件
交互模式下 = 可以直接打印变量的值
动态类型语言:
语言中没有类型定义的语法,每个值都携带了它自身的类型信息
lua将数字0和空字符串也视为“真”
只要使用双精度来表示的是一个整数,就不会出现浮点数误差问题。当今大多数cpu的浮点数运算速度和整数运算一样快,而有的cpu的浮点运算可能还要更快
lua的字符串是不可变的值,不能像在C语言中直接修改字符串的某个字符,而要根据修改要求来创建一个新的字符串
在一个字符串上应用算数操作时,lua会尝试将这个字符串转成数字
print("10" + 1) -->11
对数字做字符操作时,也会将数字转换成字符串
print(10 .. 20) -->1020
".."是字符串连接操作符,在一个数字后面输入它时,必须用一个空格来分隔,不然第一个点会被理解成小数点
table无法声明,通过构造表达式{}创建
一个持有table的变量是对table的一个引用
当没有变量引用这个table时,lua的gc会删除该table并复用它的内存
当table中的某个元素没有初始化时,它的值为nil
同样可以将nil赋值给某个元素来删除该元素
a.x等同于a["x"],是一种语法糖
函数可以存储在变量中,可以通过参数传递给其他函数,还可以作为其他函数的返回值
对于table、userdata和函数,只有它们引用同一个对象时,才认为它们相等
and操作:若第一个操作数为假,则返回第一个操作数,否则返回第二个操作数
or操作:若第一个操作数为真,则返回第一个操作数,否则返回第二个操作数
max = (x>y) and x or y
多重赋值
x,y = y,x -->交换x,y的值
a,b,c = 0 -->0,nil,nil 数量不对等,没有值的变量会被赋值为nil
数字型for
for var=exp1,exp2,exp3 do
end
for的3个表达式是在循环的一开始一次性求值的
泛型for
for k in pairs(t) do print(k) end
通过迭代器(如pairs)来遍历table
break和return只能作为一个程序块的最后一条语句或者是else end until的前一条语句
如果一定要在某段代码中间插入,则用do break end的形式来使用
一个lua函数既可以使用lua编写的函数,又可以调用以C语言编写的函数
例如所有lua标准库中的函数都是用C语言写的。但这些细节对于lua程序员来说是透明的。
无论一个函数是用lua编写还是用C语言编写,在调用它时没有任何区别
lua允许函数返回多个结果
在多重赋值中,若一个函数调用是最后的表达式,那么lua会保留其尽可能多的返回值用于匹配赋值变量,如果不是最后一个元素,则只产生一个值
可以将一个函数放入一对圆括号中,从而迫使它只返回一个结果
变长参数
function add(...)
local s = 0
for i,v in ipairs{...}
s = s + varend
return s
end
lua中的函数与所有其他值一样都是匿名的,即它们都没有名称。当讨论一个函数名是(例如print),实际上是在讨论一个持有某函数的变量
对于函数的定义
function foo(x) return 2x end
实际上是以下代码的一种简化书写方式
foo = function(x) return 2x end
因此一个函数的定义实际上是一条赋值语句
当一个函数接受另一个函数作为实参时,称其是一个高阶函数
闭包:一个函数加上该函数所需要访问的非局部的变量
do
local oldSin = math.sin
local k = math.pi/180
math.sin = function(x)
return oldSin(x*k)
end
end
将老版本的sin保存到一个私有变量中,当do end的所用域结束后,就只有新版的sin才能访问它了
当一个函数调用是另一个函数的最后一个动作时,该调用称为尾调用
function f(x) return g(x) emd
在尾调用之后,程序不需要保存任何关于该函数的栈信息。当g返回时,执行控制权可以直接返回到调用f的那个点上
使得在进行尾调用时不耗费任何栈空间
迭代器是一种可以遍历一种集合中所有元素的机制
在lua中通常将迭代器表示为函数,每调用一次函数,返回集合中的下一个元素
每个迭代器都需要在每次成功调用之间保持一些状态,这样才能知道它所在的位置及如何步进到下一个位置
一个闭包就是一种可以访问其外部嵌套环境中的局部变量的函数,对于闭包,这些变量就可以用于在成功调用之间保持状态值
function values(t) --工厂函数
local i = 0
return function() i = i + 1;return t[i] end
end
t = {10,20,30}
iter = values(t) --创建迭代器
while true do
local element = iter() --调用迭代器
if element == nil then break end
print(element)
end
泛型for
t = {10,20,30}
for element in values(t) dofile
print(element)
end
泛型for在内部保存了迭代器函数
泛型for语法:
for <val-list> in <exp-list> do
<body>
end
for做的第一件事是对in后的表达式求值,这个表达式返回3个值供for保存:
迭代器函数、恒定状态、控制变量的初值
迭代器并没有做实际的迭代,真正做迭代的是for循环,而迭代器只是为每次迭代提供一些成功后的返回值
i = 32
local i = 0
f = loadstring("i = i + 1;print(i)")
g = function() i = i + 1;print(i) end
f() -->33
g() -->1
loadstring总是在全局环境中编译它的字符串
loadstring最典型的用处是执行外部代码,可以让用户输入函数代码,来参与一个函数的定义
loadstring期望的是输入一个程序块,即一系列的语句,所以要对一个表达式求值,则必须在其之前添加return
print(debug.traceback())打印当前执行的调用栈