lua-5.1.4.tar.gz裏test目錄下有一些測試用例,我看了一遍,令人印象深刻有:
string庫的模式匹配,簡化的正則,夠用不復雜。
協程和閉包,有時可簡化編程,但有時也不好理解。
table的__index和__newindex,可用來模擬面向對象,規則雖然簡單,但用好真不那麼容易。
《Lua程序設計》中也有一些例子,這些有趣的例子和工具都列在下面:
斐波那契(fibonacci)數列
這是正着算的
-- fibfor.lua
-- example of for with generator functions
function generatefib (n)
return coroutine.wrap(function ()
local a, b = 1, 1
while a <= n do
coroutine.yield(a)
a, b = b, a+b
end
end)
end
function generatefib2 (n)
local a, b = 1, 1
return function()
if a <= n then
a, b = b, a+b
return b-a
end
end
end
for i in generatefib(1000) do print(i) end
for i in generatefib2(1000) do print(i) end
這是倒着算的
-- fib.lua
-- fibonacci function with cache
-- very inefficient fibonacci function
function fib(n)
N=N+1
if n<2 then
return n
else
return fib(n-1)+fib(n-2)
end
end
-- a general-purpose value cache
function cache(f)
local c={}
return function (n)
local y=c[n]
if not y then
y=f(n)
c[n]=y
end
return y
end
end
-- run and time it
function test(s,f,n)
N=0
local c=os.clock()
local v=f(n)
local t=os.clock()-c
print(s,n,v,t,N)
end
local n=arg[1] or 24 -- for other values, do lua fib.lua XX
n=tonumber(n)
print("","n","value","time","evals")
test("plain",fib,n)
fib=cache(fib)
test("cached",fib,n)
遞歸層數過多之後堆棧很快就會溢出,通過cache有效減少遞歸調用,關鍵是依賴解釋執行這個特性,cache可以做成通用函數,在fib成爲一個新函數之後,在原fib函數定義中對fib的調用使用的是新的fib。
篩選法求質數
-- sieve.lua
-- the sieve of of Eratosthenes programmed with coroutines
-- typical usage: lua -e N=1000 sieve.lua | column
-- generate all the numbers from 2 to n
function gen (n)
return coroutine.wrap(function ()
for i=2,n do coroutine.yield(i) end
end)
end
-- filter the numbers generated by `g', removing multiples of `p'
function filter (p, g)
return coroutine.wrap(function ()
while 1 do
local n = g()
if n == nil then return end
if math.mod(n, p) ~= 0 then coroutine.yield(n) end
end
end)
end
N=N or 1000 -- from command line
x = gen(N) -- generate primes up to N
while 1 do
local n = x() -- pick a number until done
if n == nil then break end
print(n) -- must be a prime number
x = filter(n, x) -- now remove its multiples
end
每獲得一個新質數n之後,會創建一個協程filter n,它resume filter m(m是n之前的質數)來獲取下一個數,filter m resume filter s(s是m之前的質數),…,直到resume gen產生下一個整數,filter 2判斷這個數能否被2整除,如果能整除則繼續取下一個數,不能被整除則yield給filter 3,filter 3再判斷能否被3整除,如果能則resume filter 2獲取下一個數,如果不能則yield給filter 5,…,直到最後交給filter n,最後獲得這個數,不能被已獲得的質數整除,所以它是新的質數。
這段程序首先不太好理解,其次在運算過程中嵌套層數會越來越多,堆棧很快就會溢出,也就是作爲協程的例子看看就好了。
看不懂的
-- factorial.lua
-- function closures are powerful
-- traditional fixed-point operator from functional programming
Y = function (g)
local a = function (f) return f(f) end
return a(function (f)
return g(function (x)
local c=f(f)
return c(x)
end)
end)
end
-- factorial without recursion
F = function (f)
return function (n)
if n == 0 then return 1
else return n*f(n-1) end
end
end
factorial = Y(F) -- factorial is the fixed point of F
-- now test it
function test(x)
io.write(x,"! = ",factorial(x),"\n")
end
for n=0,16 do
test(n)
end
雖然沒看懂,但我試着修改成了下面的代碼:
function F(f)
return function (n)
if n == 0 then return 1
else return n*f(n-1) end
end
end
factorial = F(function(n) return factorial(n) end)
print(factorial(4))
檢測未聲明便使用的全局變量的工具
-- strict.lua
-- checks uses of undeclared global variables
-- All global variables must be 'declared' through a regular assignment
-- (even assigning nil will do) in a main chunk before being used
-- anywhere or assigned to inside a function.
local getinfo, error, rawset, rawget = debug.getinfo, error, rawset, rawget
local mt = getmetatable(_G)
if mt == nil then
mt = {}
setmetatable(_G, mt)
end
mt.__declared = {}
local function what ()
local d = getinfo(3, "S")
return d and d.what or "C"
end
mt.__newindex = function (t, n, v)
if not mt.__declared[n] then
local w = what()
if w ~= "main" and w ~= "C" then
error("assign to undeclared variable '"..n.."'", 2)
end
mt.__declared[n] = true
end
rawset(t, n, v)
end
mt.__index = function (t, n)
if not mt.__declared[n] and what() ~= "C" then
error("variable '"..n.."' is not declared", 2)
end
return rawget(t, n)
end
測試如下:
lua -e ‘print(a);b=2’
lua -lstrict -e ‘print(a)’
lua -e ‘function f() b=2 end f()’
lua -lstrict -e ‘function f() b=2 end f()’
每次GC後運行一個函數
《Lua程序設計》第4版23.6析構器中有個有趣的例子,可以在每次GC後運行一個函數:
do
local mt = {__gc = function(o)
print("garbage collect")
setmetatable({}, getmetable(o))
end}
setmetatable({}, mt)
end
collectgarbage()
collectgarbage()
這段代碼在5.3版本有效,在5.1版本不可用,參考手冊說:
當你爲一個對象設置元表時,若此刻這張元表中用一個以字符串 “__gc” 爲索引的域,那麼就標記了這個對象需要觸發終結器。 注意:如果你給對象設置了一個沒有 __gc 域的元表,之後纔給元表加上這個域, 那麼這個對象是沒有被標記成需要觸發終結器的。 然而,一旦對象被標記, 你還是可以自由的改變其元表中的 __gc 域的。
當一個被標記的對象成爲了垃圾後, 垃圾收集器並不會立刻回收它。 取而代之的是,Lua 會將其置入一個鏈表。 在收集完成後,Lua 將遍歷這個鏈表。 Lua 會檢查每個鏈表中的對象的 __gc 元方法:如果是一個函數,那麼就以對象爲唯一參數調用它; 否則直接忽略它。
數據序列化
《Lua程序設計》第4版15.2序列化一節挺有趣,將數據序列化成自解釋的文件,加載時直接調用loadfile函數即可,以下是序列化的實現,大部分情況下它是可用的,但如果一個表的鍵不是合法的Lua標識符,或錶帶有循環,就會有問題。
#!/usr/bin/env lua
local function serialize(o, prefix, header, tailer)
if header then io.write(header) end
local t = type(o)
--from lua 5.3.3
if t=='number' or t=='string' or t=='boolean' or t=='nil' then
io.write(string.format('%q', o))
elseif t=='table' then
io.write('{\n')
local newprefix = prefix..'\t'
for k,v in pairs(o) do
if type(k) == 'number' then
io.write(newprefix, '[', k, '] = ')
else
io.write(newprefix, k, ' = ')
end
serialize(v, newprefix)
io.write(',\n')
end
io.write(prefix, '}')
else
error('cannot serialize a '..t)
end
if tailer then io.write(tailer) end
end
local function pack(o, header)
serialize(o, '', header, '\n')
end
t={v=1,[2]={y1=3,y2={z1=4,z2=5}}}
t[1]='hello world'
y=1/3
pack(y, 'y = ')
pack(t, 't = ')
繪製娥眉月
《Lua程序設計》第4版9.4函數式編程中有一個繪製娥眉月的例子:
#! /bin/env lua
--將繪圖區域(-1,-1)--(1,1)映射到區域(1,1)--(M,N),
--生成pbm格式位圖.
--遍歷(1,1)--(M,N)之間的所有像素點, 找到其在
--繪圖區域(-1,-1)--(1,1)的對應座標, 通過r判斷其亮暗.
local function plot(r, M, N)
io.output('crescent.pbm')
--pbm固定格式
io.write('P1\n', M, ' ', N, '\n')
for j=1,N do
--因爲pbm文件中行數往下增長,區域(1,1)--(M,N)
--中行數往上增長,所以取個相反數
local y=1-(j-1)*2/N
for i=1,M do
local x=(i-1)*2/M-1
io.write(r(x,y) and '0' or '1')
end
io.write('\n')
end
end
--返回判斷座標是不是在(cx,cy),r的圓內的函數
local function disk(cx, cy, r)
return function(x, y)
return (x-cx)^2 + (y-cy)^2 < r^2
end
end
--返回判斷(x,y)是否在集合s1內但不在s2內的函數
local function difference(s1, s2)
return function(x, y)
return s1(x, y) and not s2(x, y)
end
end
plot(difference(disk(0,0,1), disk(0.3,0,1)), 600, 400)
將生成的文件在jinaconvert(這個網站可以將您的圖片轉換成多種圖片格式)轉成png格式如下: