记录所有需要记录的
- 逻辑运算符认为 false 和 nil 是假(false),其他为真,0 也是 true
- string是不可变量
- Lua 语法要求 break 和 return 只能出现在 block 的结尾一句(也就是说:作为 chunk的最后一句,或者在 end 之前,或者 else 前,或者 until 前)
- 举个具体的例子,如果我们只想要 string.find 返回的第二个值。一个典型的方法是使用哑元(dummy variable,下划线):
local _, x = string.find(s, p)
-- now use `x'
-
命名参数,Lua 语法支持函数调用时实参可以是表的构造
rename{old="temp.lua", new="temp1.lua"}
function rename (arg)
return os.rename(arg.old, arg.new)
end
- Lua 中的函数是带有词法定界(lexical scoping)的第一类值(first-class values)。
- 第一类值指:在 Lua 中函数和其他值(数值、字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。
- 词法定界指:嵌套的函数可以访问他外部函数中的变量。这一特性给 Lua 提供了强大的编程能力。 我们称作外部的局部变量(external local variable)或者 upvalue。
闭包是一个函数以及它的 upvalues。
- upvalue:嵌套函数的外部函数的局部变量。匿名函数使用 upvalue i 保存他的计数,当我们调用匿名函数的时候 i 已经超出了作用范围,因为创建 i 的函数 newCounter 已经返回了。然而 Lua 用闭包的思想正确处理了这种情况。
c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2
c1、c2 是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包。
- 6.3 正确的尾调用(PPrrooppeerr TTaaiill CCaallllss)
Lua 中函数的另一个有趣的特征是可以正确的处理尾调用(proper tail recursion,一些书使用术语“尾递归”,虽然并未涉及到递归的概念)。
尾调用是一种类似在函数结尾的 goto 调用,当函数最后一个动作是调用另外一个函数时,我们称这种调用尾调用。例如:
function f(x)
return g(x)
end
g 的调用是尾调用。
例子中 f 调用 g 后不会再做任何事情,这种情况下当被调用函数 g 结束时程序不需要返回到调用者f;所以尾调用之后程序不需要在栈中保留关于调用者的任何信息。一些编译器比如 Lua 解释器利用这种特性在处理尾调用时不使用额外的栈,我们称这种语言支持正确的尾调用,由于尾调用不需要使用栈空间,那么尾调用递归的层次可以无限制的.
可以将尾调用理解成一种 goto 不是传统的函数调用
- 7.1 迭代器与闭包
看一个稍微复杂一点的例子:我们写一个迭代器遍历一个文件内的所有匹配的单词。为了实现目的,我们需要保留两个值:当前行和在当前行的偏移量,我们使用两个外部局部变量 line、pos 保存这两个值
function allwords()
local line = io.read() -- current line
local pos = 1 -- current position in the line
return function () -- iterator function
while line do -- repeat while there are lines
local s, e = string.find(line, "%w+", pos)
if s then -- found a word?
pos = e + 1 -- next position is after this word
return string.sub(line, s, e) -- return the word
else
line = io.read() -- word not found; try next line
pos = 1 -- restart from first position
end
end
return nil -- no more lines: end of traversal
end
end
for word in allwords() do
print(word)
end
------------------------ 闭包 ----------------------
lua_pushcclosure 向lua中注册c函数的过程
向lua中注册c函数的过程是通过lua_pushcclosure(L, f, n)函数实现的
流程: 1. 创建一个 sizeof(CClosure) + (n - 1) * sizeof(TValue)大小的内存, 这段内存是 CClosure + TValue[n], 并做gc簿记[这点太重要了, 为什么lua要控制自己世界中的所有变量, 就是因为它要做gc簿记来管理内存], isC= 1 标示其是一个C闭包.
2. c->f = f绑定c函数. --------- 闭包.功能抽象 = f
3. env = 当前闭包的env[这说明了被创建的闭包继承了创建它的闭包的环境]. ----------- 闭包.env = env
4. 把栈上的n个元素赋值到c->upvalue[]数组中, 顺序是越先入栈的值放在upvalue数组的越开始位置, c->nupvalues指定改闭包upvalue的个数. ---------- 闭包.upvalue = upvalue
5. 弹出栈上n个元素, 并压入新建的Closure到栈顶.
关于lua的闭包(Closure)和Upvalue
upvalue:嵌套函数的外部函数的局部变量
function func(a) <== 这个函数返回值是一个函数
return function ()
a = a + 1 <== 这里可以访问外部函数func的局部变量a,这个变量a就是upvalue
return a
end
end
func返回一个匿名函数,可用变量接取之。该匿名函数有一个upvalue a(有点像C函数的static变量),初值为首次调用func时的参数
闭包:一个匿名函数加上其可访问的upvalue
c = func(1) <== c现在指向一个拥有upvalue a = 1的匿名函数,c也被称作一个闭包
c() <== 返回2
c() <== 返回3
c2 = func(1) <== c2现在指向另外一个拥有upvalue a = 1的匿名函数,c2也被称作一个闭包
c2() <== 返回2
下面是示例代码:
int
generatecclosure(lua_State *L)
{
lua_pushnumber(L, 0);
lua_pushnumber(L, 0);
lua_pushcclosure(L, cclosure, 2);
return 1;
}
int
cclosure(lua_State *L)
{
double upval1, upval2;
upval1 = lua_tonumber(L, lua_upvalueindex(1));
upval2 = lua_tonumber(L, lua_upvalueindex(2));
upval1++; upval2++;
lua_pushnumber(L, upval1); lua_replace(L, lua_upvalueindex(1));
lua_pushnumber(L, upval2); lua_replace(L, lua_upvalueindex(2));
lua_pushnumber(L, upval1 + upval2);
return 1;
}
------------------------------------------------------------------------
- 范性 for 的执行过程:
- 首先,初始化,计算 in 后面表达式的值,表达式应该返回范性 for 需要的三个值:
迭代函数、状态常量、控制变量;与多值赋值一样,如果表达式返回的结果个数不足三个会自动用 nil 补足,多出部分会被忽略。
- 第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
- 第三,将迭代函数返回的值赋给变量列表
- 第四,如果返回的第一个值为 nil 循环结束,否则执行循环体。
- 第五,回到第二步再次调用迭代函数。
更具体地说:
for var_1, ..., var_n in explist do block end
等价于
do
local _f, _s, _var = explist
while true do
local var_1, ... , var_n = _f(_s, _var)
_var = var_1
if _var == nil then break end
block
end
end
-
8.3 错误
对于程序逻辑上能够避免的异常,以抛出错误的方式处理之,否则返回错误代码
-
协程
协同看起来只是一种复杂的调用函数的方式,真正的强大之处体现在yield 函数,它可以将正在运行的代码挂起。Lua 中协同的强大能力,还在于通过 resume-yield 来交换数据。
Lua的协同称为不对称协同(asymmetric coroutines),指“挂起一个正在执行的协同函数”与“使一个被挂起的协同再次执行的函数”是不同的,有些语言提供对称协同(symmetric coroutines),即使用同一个函数负责“执行与挂起间的状态切换
-
(直到 Lua 4.0 为止,所有的比较运算符被转换成一个,a <= b 转为 not (b < a)。然而这种转换并不一致正确。当我们遇到偏序(partial order)情况,也就是说,并不是所有的元素都可以正确的被排序情况。例如,在大多数机器上浮点数不能被排序,因为他的值不是一个数字(Not a Number 即 NaN)。根据 IEEE 754 的标准,NaN 表示一个未定义的值,比如 0/0 的结果。该标准指出任何涉及到 NaN 比较的结果都应为 false。也就是说,NaN <= x 总是 false,x < NaN 也总是 false。这样一来,在这种情况下 a <= b 转换为 not (b < a)就不再正确了。)
-
但相等比较从来不会抛出错误,如果两个对象有不同的 metamethod,比较的结果为false,甚至可能不会调用 metamethod。这也是模仿了 Lua 的公共的行为,因为 Lua 总是认为字符串和数字是不等的,而不去判断它们的值。仅当两个有共同的 metamethod 的对象进行相等比较的时候,Lua 才会调用对应的 metamethod。只需要<=就可以了
在我们关于基和操作的例子中,有类似的问题存在。<=代表集合的包含:a <= b表示集合a是集合b的子集。这种意义下,可能a <= b和b < a都是false;因此,我们需要将__le和__lt的实现分开:
Set.mt.__le = function (a,b) -- set containment
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
Set.mt.__lt = function (a,b)
return a <= b and not (b <= a)
end
最后,我们通过集合的包含来定义集合相等:
Set.mt.__eq = function (a,b)
return a <= b and b <= a
end
-
(注意:print 函数总是调用 tostring 来格式化它的输出)。
-
当格式化一个对象的时候,tostring 会首先检查对象是否存在一个带有__tostring 域的 metatable。如果存在则以对象作为参数调用对应的函数来完成格式化,返回的结果即为 tostring 的结果。
-
假定你想保护你的集合使其使用者既看不到也不能修改 metatables。如果你对 metatable 设置了__metatable 的值,getmetatable 将返回这个域的值,而调用 setmetatable 将会出错
-
__index metamethod 在继承中的使用非常常见,所以 Lua 提供了一个更简洁的使用方式。__index metamethod 不需要非是一个函数,他也可以是一个表。但它是一个函数的时候,Lua 将 table 和缺少的域作为参数调用这个函数;当他是一个表的时候,Lua 将在这个表中看是否有缺少的域。像__index 一样,如果 __newindex 的 metamethod 是一个表,解释器对指定的那个表,而不是原始的表进行赋值操作。
-
可以使用 setfenv 函数来改变一个函数的环境。Setfenv 接受函数和新的环境作为参数。除了使用函数本身,还可以指定一个数字表示栈顶的活动函数。数字 1 代表当前函数,数字 2 代表调用当前函数的函数
下面这段代码是企图应用 setfenv 失败的例子:
a = 1 -- create a global variable
-- change current environment to a new empty table
setfenv(1, {})
print(a)
导致:
stdin:5: attempt to call global `print' (a nil value)
一旦你改变了你的环境,所有全局访问都使用这个新的表,如果她为空,你就丢失所有你的全局变量,甚至_G,所以,你应该首先使用一些有用的值封装(populate)她,比如老的环境:
a = 1 -- create a global variable
-- change current environment
setfenv(1, {_G = _G})
_G.print(a) --> nil
_G.print(_G.a) --> 1
现在,当你访问"global" _G,他的值为旧的环境,其中你可以使用 print 函数。
你也可以使用继承封装(populate)你的新的环境:
a = 1
local newgt = {} -- create new environment
setmetatable(newgt, {__index = _G})
setfenv(1, newgt) -- set it
print(a) --> 1
在这段代码新的环境从旧的环境中继承了 print 和 a;然而,任何赋值操作都对新表进行,不用担心误操作修改了全局变量表。另外,你仍然可以通过_G 修改全局变量:
-- continuing previous code
a = 10
print(a) --> 10
print(_G.a) --> 1
_G.a = 20
print(_G.a) --> 20
当你创建一个新的函数时,他从创建他的函数继承了环境变量
-
中间有nil的数组,#操作是不可靠的
-
Lua 中的特殊字符如下:( ) . % + - * ? [ ^ $
‘%’ 用作特殊字符的转义字符,因此 ‘%.’ 匹配点;’%%’ 匹配字符 ‘%’。转义字符 '%'不仅可以用来转义特殊字符,还可以用于所有的非字母的字符。当对一个字符有疑问的时候,为安全起见请使用转义字符转义他
-
io.write 、io.read写入和读取标准输入输出中的一行
在编写代码时应当避免像 io.write(a…b…c);这样的书写,这同 io.write(a,b,c)的效果是一样的。但是后者因为避免了串联操作,而消耗较少的资源。原则上当你进行粗略(quick and dirty)编程,或者进行排错时常使用 print 函数。当需要完全控制输出时使用 write。
-
Write 函数与 print 函数不同在于,write 不附加任何额外的字符到输出中去,例如制表符,换行符等等。还有 write 函数是使用当前输出文件,而 print 始终使用标准输出。
另外print函数会自动调用参数的tostring方法,所以可以显示出表(tables)函数(functions)和 nil
-
read 函数从当前输入文件读取串,由它的参数控制读取的内容:
- “*all” 读取整个文件
- “*line” 读取下一行
- “*number” 从串中转换出一个数值
- num 读取 num 个字符到串
io.read("*all")函数从当前位置读取整个输入文件。如果当前位置在文件末尾,或者文件为空,函数将返回空串。特别的,io.read(0)函数的可以用来测试是否到达了文件末尾。如果不是返回一个空串,如果已是文件末尾返回 nil
-
I/O 库提供三种预定义的句柄:io.stdin、io.stdout和 io.stderr。因此可以用如下代码直接发送信息到错误流(error stream)。
io.stderr:write(message)
int lua_gettop (lua_State *L); 函数 lua_gettop 返回堆栈中的元素个数,它也是栈顶元素的索引。注意一个负数索引-x 对应于正数索引 gettop-x+1。
void lua_settop (lua_State *L, int index); lua_settop 设置栈顶(也就是堆栈中的元素个数)为一个指定的值。如果开始的栈顶高于新的栈顶,顶部的值被丢弃。否则,为了得到指定的大小这个函数压入相应个数的空值(nil)到栈上。
void lua_pushvalue (lua_State *L, int index); 函数 lua_pushvalue 将堆栈上指定索引处的值复制一份到栈顶
void lua_remove (lua_State *L, int index); lua_remove 移除指定索引位置的元素,并将其上面所有的元素下移来填补这个位置的空白
void lua_insert (lua_State *L, int index); lua_insert 移动栈顶元素到指定索引的位置,并将这个索引位置上面的元素全部上移至栈顶被移动留下的空隔
void lua_replace (lua_State *L, int index); lua_replace 从栈顶弹出元素值并将其设置到指定索引位置,没有任何移动操作
Lua 5.3 参考手册 各API介绍 https://cloudwu.github.io/lua53doc/contents.html
所有元方法:
元方法
__index
__newindex
__pairs
__len
__tostring __tostring 元方法用于修改表的输出行为
__call __call 元方法在 Lua 调用一个值时调用
__metatable
__gc userdata专用
__concat – ‘…’
其他
__add 对应的运算符 ‘+’.
__sub 对应的运算符 ‘-’.
__mul 对应的运算符 ‘*’.
__div 对应的运算符 ‘/’.
__mod 对应的运算符 ‘%’.
__unm 对应的运算符 ‘-’.
__concat 对应的运算符 ‘…’.
__eq 对应的运算符 ‘==’.
__lt 对应的运算符 ‘<’.
__le 对应的运算符 ‘<=’.
|
请发表评论