Lua几个例子

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格式如下:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章