首先调用迭代工厂;内部保留迭代函数,所以咱们不须要 iter 变量;而后在每个新的迭代处调用迭代器函数;当迭代器返回 nil 时循环结束。
在循环过程当中范性 for 在本身内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。程序员
范性 for 的执行过程:闭包
具体举例:函数
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
无状态的迭代器是指不保留任何状态的迭代器,所以在循环中咱们能够利用无状态迭代器避免建立闭包花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量和控制变量)的值做为参数被调用,一个无状态的迭代器只利用这两个值能够获取下一个元素。学习
本身实现ipairs举例:this
a = {"one", "two", "three"} function iter (a, i) i = i + 1 local v = a[i] if v then return i, v end end function ipairs (a) return iter, a, 0 -- 迭代函数、状态常量、控制变量 end for i, v in ipairs(a) do print(i, v) end
Lua 库中实现的 pairs 是一个用 next 实现的原始方法:lua
function pairs (t) return next, t, nil end for k, v in next, t do ... end
不少状况下,迭代器须要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,还有一种方法就是将全部的状态信息封装到 table 内,将 table做为迭代器的状态常量,由于这种状况下能够将全部的信息存放在 table 内,因此迭代函数一般不须要第二个参数。prototype
咱们应该尽量的写无状态的迭代器,由于这样循环的时候由 for 来保存状态,不须要建立对象花费的代价小;若是不能用无状态的迭代器实现,应尽量使用闭包;尽量不要使用 table 这种方式,由于建立闭包的代价要比建立 table 小,另外 Lua 处理闭包要比处理 table 速度快些。后面咱们还将看到另外一种使用协同来建立迭代器的方式,这种方式功能更强但更复杂。代理
多状态迭代器举例:code
local iterator function allwords() local state = {line=io.read(),pos=1 } return iterator,state -- 返回迭代函数,状态常量 end function iterator(state) while state.line do local s,e = string.find(state.line,"%w+",state.pos) if s then state.pos=e+1 return string.sub(state.line,s,e) else state.line=io.read() state.pos=1 end end return nil end for i in allwords() do print(i) end --function allwords(f) -- for l in io.lines() do -- for w in string.gfind(l, "%w+") do -- f(w) -- end -- -- end -- --end --allwords(print)
举例:orm
list=nil -- 链表最末尾节点 list={next=list,value=v} -- 链表建立时往前赋值 local l=list while l do -- 遍历链表 print(l.value) l=l.next end
举例:
List={} function List.new() return {first=0,last=-1} end function List.pushleft(list,value) local first=list.first-1 list.first=first list[first]=value end function List.pushright(list,value) local last=list.last+1 list.last=last list[last]=value end function List.popleft(list) local first=list.first if first>list.last then error("list is empty") end local value = list[first] list[first]=nil list.first=first+1 return value end function List.popright(list) local last=list.last if last < list.first then error("list is empty") end local value=list[last] list[last]=nil list.last=list.last-1 return value end local a =List.new() List.pushright(a,1) List.pushright(a,2) List.pushright(a,3) List.pushright(a,4) print(List.popright(a)) -- 4 print(List.popright(a)) -- 3 print(List.popright(a)) -- 2 print(List.popright(a)) -- 1
举例:
function Set(list) local set={} for _,l in ipairs(list) do set[l]=true end return set end reserved = Set{"while", "end", "function", "local", "local","while"} -- 已经将table中的元素去重了 for k,v in pairs(reserved)do io.write(k," ") end -- local function end while
递归实现table的序列化。
举例:
function serialize(o) if type(o)=="number" then -- 若是是number则直接写入 io.write(o) elseif type(o)=="string" then -- 若是是string经过format写入,用%q可使用双引号表示字符串而且能够正确的处理包含引号和换行等特殊字符的字符串 io.write(string.format("%q",o)) elseif type(o)=="table" then -- 若是是table则遍历table中的元素,递归调用serialize io.write("{\n") for k,v in pairs(o) do -- io.write(" ",k," = ") io.write("[") serialize(k) io.write("] = ") serialize(v) io.write(",\n") end io.write("}\n") else error("can not serialize a " .. type(o)) end end a={a="1",b="2",c={1,2,3,4,5},"q","w","e","r","t","y","u" } serialize(a) -- { -- [1] = "q", -- [2] = "w", -- [3] = "e", -- [4] = "r", -- [5] = "t", -- [6] = "y", -- [7] = "u", -- ["b"] = "2", -- ["c"] = { -- [1] = 1, -- [2] = 2, -- [3] = 3, -- [4] = 4, -- [5] = 5, -- } -- , -- ["a"] = "1", -- }
Metatables 容许咱们改变 table 的行为,例如,使用 Metatables 咱们能够定义 Lua 如何计算两个 table 的相加操做 a+b。当 Lua 试图对两个表进行相加时,他会检查两个表是否有一个表有 Metatable,而且检查 Metatable 是否有__add 域。若是找到则调用这个__add函数(所谓的 Metamethod)去计算结果。
举例:
Set={} Set.mt={} function Set.new(t) local set={} setmetatable(set,Set.mt) for _,l in ipairs(t) do set[l]=true end return set end function Set.union(a,b) if getmetatable(a)~=Set.mt or getmetatable(b)~=Set.mt then error("attempt to `add' a set with a non-set value",2) end local res=Set.new{} for k in pairs(a) do res[k]=true end for k in pairs(b) do res[k]=true end return res end function Set.intersection(a,b) -- 取a,b的交集 local res=Set.new{} for k in pairs(a) do res[k]=b[k] end return res end function Set.tostring(set) local s="{" local sep="" for e in pairs(set) do s=s..sep..e sep=", " end return s.."}" end function Set.print(s) print(Set.tostring(s)) end Set.mt.__add=Set.union Set.mt.__mul=Set.intersection s1=Set.new {10,20,30,40,50} s2=Set.new {30,40,50,60,70} print(getmetatable(s1)) -- table: 0x0004b540 print(getmetatable(s2)) -- table: 0x0004b540 s3=s1+s2 Set.print(s3) -- {60, 20, 10, 70, 50, 40, 30} Set.print((s1+s2)*s1) -- {50, 30, 20, 40, 10} -- 对于每个算术运算符,metatable 都有对应的域名与其对应,除了__add、__mul 外,还有__sub(减)、__div(除)、__unm(负)、__pow(幂), -- 咱们也能够定义__concat 定义 链接行为。__call能够将table按照函数的方法调用,触发__call对应的函数执行 Set.print(s1+8) --/usr/local/bin/luajit: metatable/metamethods1.lua:64: attempt to `add' a set with a non-set value --stack traceback: --[C]: in function 'error' --metatable/metamethods1.lua:19: in function '__add' --metatable/metamethods1.lua:64: in main chunk --[C]: at 0x0100001770
Lua 选择 metamethod 的原则:若是第一个参数存在带有__add 域的 metatable, Lua使用它做为 metamethod,和第二个参数无关;不然第二个参数存在带有__add 域的 metatable, Lua 使用它做为 metamethod 不然报错。
举例:
Set={} Set.mt={} function Set.new(t) local set={} setmetatable(set,Set.mt) for _,l in ipairs(t) do set[l]=true end return set end function Set.union(a,b) if getmetatable(a)~=Set.mt or getmetatable(b)~=Set.mt then error("attempt to `add' a set with a non-set value",2) end local res=Set.new{} for k in pairs(a) do res[k]=true end for k in pairs(b) do res[k]=true end return res end function Set.intersection(a,b) -- 取a,b的交集 local res=Set.new{} for k in pairs(a) do res[k]=b[k] end return res end function Set.tostring(set) local s="{" local sep="" for e in pairs(set) do s=s..sep..e sep=", " end return s.."}" end function Set.print(s) print(Set.tostring(s)) end Set.mt.__add=Set.union Set.mt.__mul=Set.intersection Set.mt.__tostring=Set.tostring -- 特殊方法,print会调用metatable的tostring方法返回的字符串 Set.mt.__metatable = "not your business" -- 特殊方法,加上这个方法后,就没法访问对象的metatable以及修改了 Set.mt.__le=function(a,b) 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 s1=Set.new {10,20,30,40,50,60,70} s2=Set.new {30,40,50,60,70 } print(s1) -- {50, 30, 60, 70, 20, 40, 10} print(s2) -- {50, 70, 40, 60, 30} print(s1<=s2) -- false print(s1<s2) -- false print(s1>=s2) -- true print(s1>s2) -- true print(s1==s1*s2) -- false print(getmetatable(s1)) -- not your business print(setmetatable(s1,{})) --/usr/local/bin/luajit: metatable/metamethods2.lua:82: cannot change a protected metatable --stack traceback: -- [C]: in function 'setmetatable' -- metatable/metamethods2.lua:82: in main chunk -- [C]: at 0x0100001770
举例:
-- 经过__index设置默认值 -- 访问一个表的不存在的域,触发 lua 解释器去查找__index metamethod:若是不存在, 返回结果为 nil;若是存在则由__index metamethod 返回结果。 window={} window.prototype={x=0,y=0,width=100,height=100 } window.mt={} function window.new(o) setmetatable(o,window.mt) return o end --window.mt.__index=function(table,key) -- return window.prototype[key] --end window.mt.__index=window.prototype -- 这样也能够,就不用特地定义一个匿名函数了 w=window.new({x=10,y=20}) print(w.width) -- 100 # 先寻找w中,没找到width属性,去它的metatable里面调用__index方法,若是找到则返回,若是没找到返回nil print(rawget(w,width)) -- nil # 不会经过__index的方式去获取w中的属性,若是没有,直接返回nil -- __newindex metamethod用来对表更新,__index则用来对表访问。当你给表的一个 缺乏的域赋值, -- 解释器就会查找__newindex metamethod:若是存在则调用这个函数而不进行赋值操做。调用 rawset(t,k,v)不调用任何 metamethod 对表 t 的 k 域赋值为 v。 ------------------------------------------------------------------------------------------------------------------------ -- 使用key为{}隐藏默认值 local key={} local mt={__index=function(t) return t[key] end } -- 返回t[{}]的值,意思是若是没有找到key,则调用__index,永远返回t[{}]的值 function setDefault(t,d) t[key]=d -- t[{}]=d setmetatable(t,mt) -- 设置mt为metatable end table={x=10,y=20 } print(table.x,table.z) -- 10 nil # 访问z找不到值 setDefault(table,0) print(table.x,table.z) -- 10 0 # 访问z以前已经设置了默认值 ------------------------------------------------------------------------------------------------------------------------ -- 监控table的实现 local index={} local mt={ __index=function(t,k) print("*access to element" .. tostring(k)) return t[index][k] end, __newindex=function(t,k,v) print("update of element " .. tostring(k) .. " to " .. tostring(v)) t[index][k]=v end } function track(t) local proxy={} -- 建立代理table proxy[index]=t -- 将原始table赋值到代理table的{}键上,外部不能访问 setmetatable(proxy,mt) -- 将mt设置到proxy的metatable上,当从proxy读取不到值的时候就必然会调用metatable的__index,就能够实现监控 return proxy end table={x=10,y=20} table=track(table) print(table.x) -- *access to elementx -- 10 table.z=30 -- update of element z to 30 ------------------------------------------------------------------------------------------------------------------------ -- 只读__index实现 function readOnly(t) local proxy={} local mt={ __index=t, __newindex=function() -- 控制修改表,保持Proxy里面是空的,那么修改表就必定会走__newindex方法 error("attempt to update a read-only table",2) end } setmetatable(proxy,mt) return proxy end days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} print(days[2]) -- Monday days[2]="Noday" --/usr/local/bin/luajit: metatable/metamethods3.lua:91: attempt to update a read-only table --stack traceback: --[C]: in function 'error' --metatable/metamethods3.lua:80: in function '__newindex' --metatable/metamethods3.lua:91: in main chunk --[C]: at 0x0100001770
Lua 的全部协同函数存放于 coroutine table 中。 create 函数用于建立新的协同程序,其只有一个参数:一个函数,即协同程序将要运行的代码。若一切顺利,返回值为 thread类型,表示建立成功。一般状况下, create 的参数是匿名函数。
协程有三个状态:挂起态(suspended)、运行态(running)、中止态(dead)。建立协同程序成功时,其为挂起态,即此时协同程序并未运行。咱们可用 status 函数检查协同的状态。
举例:
-- 协程建立,状态,执行 co= coroutine.create(function() -- 建立协程,此时处于挂起状态 print("hello world") end) print(co) -- thread: 0x0004afa0 print(coroutine.status(co)) -- suspended coroutine.resume(co) -- hello world -- 运行协程,获得想要的输出。resume 运行在保护模式下,所以,若是协同程序内部存在错误, Lua 并不会抛出错误,而是将错误返回给 resume 函数。 print(coroutine.status(co)) -- dead -- 协程结束,检查状态为dead
举例:
-- yield挂起协程执行 co = coroutine.create(function() for i = 1,10 do print("co",i) coroutine.yield() end end) coroutine.resume(co) -- co 1 print(coroutine.status(co)) -- suspended coroutine.resume(co) -- co 2 coroutine.resume(co) -- co 3 coroutine.resume(co) -- co 4 coroutine.resume(co) -- co 5 coroutine.resume(co) -- co 6 coroutine.resume(co) -- co 7 coroutine.resume(co) -- co 8 coroutine.resume(co) -- co 9 coroutine.resume(co) -- co 10 print(coroutine.resume(co)) -- true # 最后一个yield后面的代码被执行,这时候完全执行完了该协程 print(coroutine.resume(co)) -- false cannot resume dead coroutine # 再去执行协程,返回false,提示报错 协程已经到了dead状态 -- yield传递参数 co=coroutine.create(function(a,b,c) coroutine.yield(a+b,a-c) end) print(coroutine.resume(co,1,2,3)) -- true 3 -2 # 返回yield传回来的结果 print(coroutine.resume(co)) -- true print(coroutine.resume(co)) -- false cannot resume dead coroutine co=coroutine.create(function() print("co",coroutine.yield()) -- co 1 2 # 传递参数给yield a,b=coroutine.yield() print(a,b) -- 1 2 end) coroutine.resume(co) coroutine.resume(co,1,2) coroutine.resume(co,1,2) -- return结果返回 co=coroutine.create(function() return 4,5 end) print(coroutine.resume(co)) -- true 4 5 # return的结果被resume接收了
举例:
-- 协同是一种非抢占式的多线 程。管道的方式下,每个任务在独立的进程中运行,而协同方式下,每一个任务运行在 独立的协同代码中。 -- 管道在读(consumer)与写(producer)之间提供了一个缓冲,所以 二者相关的的速度没有什么限制,在上下文管道中这是很是重要的, -- 由于在进程间的切 换代价是很高的。协同模式下,任务间的切换代价较小,与函数调用至关,所以读写可 以很好的协同处理。 function receive(prod) -- 执行协程,将拿到结果返回 local status,value=coroutine.resume(prod) return value end function send(x) -- yield 夯住,等待下次调用 coroutine.yield(x) end function producer() -- 建立一个协程,读取输入,并调用send,返回输入的值,并夯住 return coroutine.create(function() while true do local x=io.read() send(x) end end) end function filter(prod) -- 建立一个协程,调用receive执行producer建立的协程,拿到结果后格式化,后调用send,返回格式化后的结果,并夯住 return coroutine.create(function() local line = 1 while true do local x= receive(prod) x=string.format("%5d %s",line,x) send(x) line=line+1 end end) end function consumer(prod) -- 循环调用receive执行filter建立的协程,并接受返回结果,打印出来 while true do local x=receive(prod) io.write(x,"\n") end end consumer(filter(producer())) -- 多层嵌套执行协程
举例:
-- 初版,用递归迭代器实现输出全部组合 function Permgen(a,n) if n==0 then printResult(a) else for i=1,n do -- 将列表前面全部的数值和最后一个数值替换,都能出现一种状况,而后递归调用,将遍历出全部组合的状况 a[n],a[i]=a[i],a[n] Permgen(a,n-1) a[n],a[i]=a[i],a[n] end end end function printResult(a) for i,v in ipairs(a) do io.write(v," ") end io.write("\n") end Permgen({1,2,3},3) --2 3 1 --3 2 1 --3 1 2 --1 3 2 --2 1 3 --1 2 3 ------------------------------------------------------------------------------------------------------------------------- -- 使用协程替换迭代器实现 function Permgen(a,n) if n==0 then coroutine.yield(a) else for i=1,n do a[n],a[i]=a[i],a[n] Permgen(a,n-1) a[n],a[i]=a[i],a[n] end end end function printResult(a) for i,v in ipairs(a) do io.write(v," ") end io.write("\n") end --function perm(a) -- 在这种状况下,被下面的方法替代了 -- local n=table.getn(a) -- local co=coroutine.create(function()Permgen(a,n) end) -- return function() -- local code,res=coroutine.resume(co) -- return res -- end --end -- 通常状况下,coroutine.wrap 比 coroutine.create 使用起来简单直观,前者更确切的提 供了咱们所须要的:一个能够 resume 协同的函数, -- 然而缺乏灵活性,没有办法知道 wrap 所建立的协同的状态,也没有办法检查错误的发生。 function perm (a) local n = table.getn(a) return coroutine.wrap(function () Permgen(a, n) end) end for p in perm{"a","b","c"} do printResult(p) end
Lua 不存在类的概念,每一个对象定义他本身的行为并拥有本身的形状(shape)。然而,依据基于原型(prototype)的语言好比 Self 和 NewtonScript,在 Lua中仿效类的概念并不难。 在这些语言中, 对象没有类。 相反, 每一个对象都有一个 prototype(原型),当调用不属于对象的某些操做时,会最早会到 prototype 中查找这些操做。在这类语言中实现类(class)的机制,咱们建立一个对象,做为其它对象的原型便可(原型对象为类,其它对象为类的 instance)。类与 prototype 的工做机制相同,都是定义了特定对象的行为。
举例:
-- 定义方法的时候带上一个额外的参数,来表示方法做用的对象。这个参数常常为 self 或者 this -- self 参数的使用是不少面向对象语言的要点。大多数 OO 语言将这种机制隐藏起来,这样程序员没必要声明这个参数(虽然仍然能够在方法内使用这个参数)。 -- Lua 也提供了经过使用冒号操做符来隐藏这个参数的声明。冒号的效果至关于在函数定义和函数调用的时候,增长一个额外的隐藏参数。 Account={ balance=0, withdraw=function(self,v) self.balance=self.balance-v end } function Account:deposit(v) self.balance=self.balance+v end Account.deposit(Account,200.00) Account:withdraw(100.00) print(Account.balance)
举例:
-- 类的继承 Account={balance=0 } function Account:new(o) o=o or {} setmetatable(o,self) self.__index=self return o end function Account:deposit(v) self.balance=self.balance+v end function Account:withdraw(v) if v>self.balance then error("insufficient funds") end self.balance=self.balance-v end SpecialAccount=Account:new() -- Account的子类 function SpecialAccount:withdraw(v) if v-self.balance>=self:getLimit() then error("insufficient funds") end self.balance=self.balance-v end function SpecialAccount:getLimit() return self.limit or 0 end s=SpecialAccount:new({limit=1000.00}) -- SpecialAccount的子类 function s:getLimit() -- 若是子类s中定义了getLimit,则不会调用SpecialAccount中的getLimit return self.limit*0.10 or 0 end --s:withdraw(200) -- insufficient funds --print(s.balance) -- insufficient funds -- SpecialAccount 从 Account 继承了 new 方法,当 new 执行的时候, self 参数指向SpecialAccount。因此, s 的 metatable 是 SpecialAccount, __index 也是 SpecialAccount。这样, s 继承了 SpecialAccount,后者继承了 Account。当咱们执行:s:deposit(100.00) Lua 在 s 中找不到 deposit 域,他会到 SpecialAccount 中查找,在 SpecialAccount 中找不到,会到 Account 中查找。使得 SpecialAccount 特殊之处在于,它能够重定义从父类中继承来的方法(继承和重写父类方法) ------------------------------------------------------------------------------------------------------------------------ -- 多继承 local function search(k,plist) for i=1,table.getn(plist) do local v=plist[i][k] if v then return v end end end function creatClass(...) local c={} local args={...} -- 这里注意,...虽然在lua中是表明不定参数,可是要获取这些不定参数,须要使用{...}获取,会生成一个tables,里面放了全部的参数,以及一个key为n,对应总共有多少个参数被接受。 setmetatable(c,{__index=function(t,k) -- return search(k,args) local v=search(k,args) -- 这样改造,访问到一个函数在t中没有的,则从父类中找到该方法后,将该方法赋值给子类t中, -- 加快以后访问该方法的速度,缺点是没法在以后修改父类的方法,由于修改后不会影响到子类 t[k]=v return v end}) c.__index=c function c:new(o) o=o or {} setmetatable(o,c) return o end return c end Named = {} function Named:getname() return self.name end function Named:setname(n) self.name = n end NamedAccount=creatClass(Account,Named) account=NamedAccount:new({name="Paul"}) print(account:getname())