A:“引用”既为对象的名字。比如a = {}
,名叫a
的变量存储的值是一个”table”,”table”的名字是a
;同时还可以说变量a
存储着”table”的“引用”。
当“(强)引用”被创建时,对象的“引用计数”加1,只要对象的“引用计数”不为0,对象就不会被销毁,Lua的“垃圾回收系统”只回收那些“引用计数”为0的对象。
A:当对象被创建时会导致对象的引用计数加1的引用为“强引用”。
默认情况下,Lua中的对象在被创建时都是“强引用”,
-- t中保存了"table"的“强引用”。
t = {"one", "two"}
-- t保存了新的"table"的“强引用”,原先"table"的引用计数减为0,将被销毁。
t = {"three", "four"}
顾名思义,当对象被创建时不会导致对象的引用计数加1的引用为“弱引用”。因为其特性,“弱引用”不会阻止“垃圾回收系统”回收对象。通过”Weak Tables”,Lua支持”table”中的”key”或是”value”或是两者作为“弱引用”存储。
metatable
的__mode
域可以控制具体的操作。这个域必须存储的是一个字符串,如果字符串中包含'k'
(一定要是小写的),那么”weak table”中对所有的”key”都使用“弱引用”;如果字符串中包含'v'
(小写),那么”weak table”中对所有的”value”都使用“弱引用”,
a = {}
b = {}
setmetatable(a, b) -- 将"b"设置为"a"的"metatable"。
b.__mode = "k" -- now 'a' has weak keys
key = {} -- creates first key
a[key] = 1
key = {} -- creates second key
a[key] = 2
collectgarbage() -- 强制让Lua回收垃圾。
for k, v in pairs(a) do print(v) end --> 2
这个例子中,第二个key = {}
使得变量”key”存储了新的”table”,而原先的”table”没有强引用对其引用了(只有”weak table”对其引用,而且还是弱引用)。所以当调用collectgarbage()
时,第一个”table”被垃圾收集器收集,所以”table”中只剩下了第二个”table”(这第二个”table”由变量key
对其强引用)。
A:编程中一项通用的技术就是“空间换时间”。为了给函数提速,你可以将函数的参数与其结果作为映射存储起来。当下次有相同的参数时,直接取出结果。
想象一个用Lua写的服务器,他接收请求,请求均是以字符串组成。每一次接收到一个请求,他就调用一次loadstring()
。loadstring()
是一个开销很大的函数,并且有的请求对于服务器来说会非常的频繁,比如closeconnection()
。为了避免每次收到这种频繁的请求都要调用一次loadstring()
,服务器可以使用一张辅助table来记录请求字符串与其用loadstring()
得到的结果之间的映射。在调用loadstring()
之前,服务器首先查看请求的命令是否存储在辅助表中,如果存在,则直接取出结果,如果不存在,才调用loadstring()
,并在调用完成之后将参数与结果的映射存储在辅助表中。
local results = {}
function mem_loadstring (s)
if results[s] then -- result available?
return results[s] -- reuse it
else
local res = loadstring(s) -- compute new result
results[s] = res -- save for later reuse
return res
end
end
这个方案的存储消耗可能是巨大的。尽管如此,它仍然可能会导致意料之外的数据冗余。尽管一些命令一遍遍的重复执行,但有些命令可能只运行一次。渐渐地,这个”table”积累了服务器所有命令被调用处理后的结果,早晚有一天它会挤爆服务器的内存。一个”weak table”提供了对于这个问题的简单解决方案,如果这个结果表中有”weak”值,每次的垃圾收集循环都会移除当前时间内所有未被使用的结果(通常差不多是全部):
local results = {}
setmetatable(results, {__mode = "kv"}) -- make keys and values weak
function mem_loadstring (s)
... -- as before
A:1、使用”weak table”将”table”与”default value”关联,
local defaults = {} -- "table"与"default value"之间的映射。
setmetatable(defaults, {__mode = "k"}) -- "table"可以被回收。
local mt = {__index = function (t) return defaults[t] end}
function setDefault (t, d)
defaults[t] = d
setmetatable(t, mt)
end
2、使用”weak table”将”default value”与获取”default value”的方法关联,
local metas = {} -- "default value"与获取"default value"的方法之间的映射。
setmetatable(metas, {__mode = "v"}) -- 获取"default value"的方法可以被回收。
function setDefault (t, d)
local mt = metas[d]
if mt == nil then
-- 获取"default value"的方法。
mt = {__index = function () return d end}
metas[d] = mt
end
setmetatable(t, mt)
end
A:
-- 三角函数
math.sin(x), math.cos(x), math.tan(x), math.asin(x), math.acos(x), math.atan(x),
math.deg(x) -- 弧度转角度。
math.rad(x) -- 角度转弧度。
-- 所有三角函数的参数或者返回值默认是以弧度为单位。如果你想以角度为单位,可以自行转换。
local sin, asin, ... = math.sin, math.asin, ...
local deg, rad = math.deg, math.rad
math.sin = function (x) return sin(rad(x)) end
math.asin = function (x) return deg(asin(x)) end
...
-- 幂函数、对数函数、平方函数
math.exp(x) -- 返回自然对数"e"的"x"幂。
math.log(x [, base]) -- "x"基于"base"的对数,"base"默认是自然对数"e"。
math.sqrt(x) -- 返回"x"的平方根。也可以使用"x ^ 0.5"计算。
-- 取整取余函数
math.floor(x) -- 返回小于等于"x"的最大整数。
math.ceil(x) -- 返回大于等于"x"的最小整数。
math.modf(x) -- 返回"x"的整数部分和小数部分。
math.fmod(x, y) -- 返回参数"x / y"的余数。
math.tointeger(x) -- 将"x"转换为整数(如果可以)。
-- 极值函数
math.max(x, ···), math.min(x, ···)
-- 绝对值函数
math.abs(x)
-- 数值类型函数
math.type(x) -- 如果"x"是数值则返回其类型("integer", "float"),否则返回"nil"。
-- 无符号数值比较函数
-- 如果整数"m"和"n"以无符号整数形式比较,并且"m < n"则返回"true",否则返回"false"。
math.ult(m, n)
-- 值
math.pi -- 圆周率。
math.maxinteger -- 整数的最大值。
math.huge -- C语言中定义的宏"HUGE_VAL"的值,一个大于任何数值的数(数值太大,无法表示时使用)。
math.mininteger -- 整数的最小值。
A:
--[[ 产生伪随机数,有三种调用方式:
1、不带参数,将产生"[0,1)"范围内的随机数。
2、带一个参数"m",将产生"[1, m]"范围内的随机数。
3、带两个参数"m"和"n",将产生[m, n]范围内的随机数。]]
math.random([m [, n]])
math.randomseed(x) -- 设置随机数种子。
1、Lua使用“引用计数”机制供“垃圾回收系统”自动回收无用的垃圾。Lua的“垃圾回收系统”将你从内存管理的负担中解放出来,更重要的是你不用担心与内存管理相关的”bugs”,比如野指针和内存泄露。
但即使是“最聪明”的“垃圾回收系统”也不可能让程序员完全不用关注内存管理的问题。“垃圾回收系统”只能收集他所认为的垃圾,他不知道你所认为的垃圾有哪些。一个典型的例子是栈,当你使用一个数组加上一个存储栈顶索引的变量实现一个栈时,你知道数组中从栈底到栈顶之间的数据是有效的,但Lua不知道。当你pop
一个数据,然后将栈顶索引减1的时候,刚被pop
的数据依旧在数组中,而你知道他没用了,可是Lua不知道。类似的,任何存储在全局变量中的数据对于Lua来说都不是垃圾(即使有可能你的程序不会再使用它们)。在这两种情况下,这取决于你(的程序)将这些变量赋值为nil
。
2、要注意,只有对象才可以从一个”weak table”中被收集。比如数字或字符串都是不会被收集的。此外Lua中的函数也是对象,他如果作为”weak table”中的”key”也可以被收集,
a = {}
b = {}
setmetatable(a, b) -- 将"b"设置为"a"的"metatable"。
b.__mode = "k" -- now 'a' has weak keys
a[1] = 6
a["a" .. "b"] = 7
foo = function() print() end -- first key, ignore "print()".
a[foo] = 9
foo = function() print() end -- second key, ignore "print()".
a[foo] = 10
collectgarbage() -- 强制让Lua回收垃圾。
for k, v in pairs(a) do io.write(v, " ") end --> 6 10 7
print()
关于字符串的一些细微差别:从上面的例子来看,尽管字符串是可以被收集的,但他
们仍然跟其他可收集对象有所区别。其他对象,比如”tables”和函数,他们都是显式的被创建。比如,不管什么时候当Lua遇到{}
时,它建立了一个新的”table”;任何时候遇到function() ... end
时,建立了一个新的函数(实际上是一个”Closure”)。
然而,当Lua见到"a" .. "b"
时会创建一个新的字符串?如果系统中已经有一个字符串"ab"
的话怎么办?Lua会重新建立一个新的?编译器可以在程序运行之前创建字符串吗?这无关紧要:这些是实现的细节。因此,从程序员的角度来看,字符串是值而不是对象。所以,就像数值或布尔值,一个字符串不会从”weak tables”中被移除(除非它所关联的”vaule”被收集)。
3、math.randomseed()
的参数一般为os.time()
以产生随机的种子,
math.randomseed(os.time())