这个网站是一个老外的博客,有很多关于lua和love2d的内容,可惜被墙了。这几天也不知道
如何继续love2d,便想着干脆捡一些有用的简单翻译一下,或许能产生点灵感。对于lua而言
我感觉最强大的就是它的table了,有点lisp里的表的感觉。
简译如下,原文在此。
作为一个创建自定义数据结构的例子, 我想你得先了解一些关于链表的实现 linkedlists 。
注意这并不是一篇深入的教程,大部分情况下我会展示一些代码片段。
链表是很好的数据结构,可以完美的实现高效的添加,删除,遍历。唯一的缺点是你不能通过
索引查找元素。
有多种链表,最常见的两种是单链表、双链表。各种链表都有一堆节点,并维持着指向节点之间
的索引。单链表有指向头节点的索引,每个节点都指向下一个节点。双链表有指向头节点和尾节
点的索引,每个节点都指向上一个和下一个节点。若想了解更多,参考Wikipedia page 。
我决定实现双链表。它的效率稍逊单链表,但是更好用,因为它允许逆向遍历。让我们先来
一段OOP代码。
list = {} list.__index = list setmetatable(list, { __call = function(_, ...) local t = setmetatable({}, list) for _, v in ipairs{...} do t:push(v) end end })
–译者注:setmetatable设置元表,__call标示table调用时会执行的函数
–这里调用list时就会自动执行__call,__call的内容是先绑定一个空表{},再把参数放入{}。
它允许用户想这样创建链表:
x = list(a, b, c, d)
注意所有的元素都必须是tabel,这样元素才支持索引。(译注:如同c语言里的节点是结构体)。
我展示的第一个函数式简单的 push
,它把元素放到链表的末尾。
function list:push(t) if self.last then --如果有节点 self.last._next = t t._prev = self.last self.last = t else --如果是空链表 self.first = t self.last = t end self.length = self.length + 1 end
下面是具有相反功能的pop
函数, 它把链表里末尾的元素删除,并返回其值。
function list:pop() if not self.last then return end local ret = self.last if ret._prev then ret._prev._next = nil self.last = ret._prev ret._prev = nil else -- this was the only node self.first = nil self.last = nil end self.length = self.length - 1 return ret end
我们使用的更灵活的添加和删除函数式 insert
和remove:
function list:insert(t, after) if after then if after._next then after._next._prev = t t._next = after._next else self.last = t end t._prev = after after._next = t self.length = self.length + 1 elseif not self.first then -- this is the first node self.first = t self.last = t end end function list:remove(t) if t._next then if t._prev then t._next._prev = t._prev t._prev._next = t._next else -- this was the first node t._next._prev = nil self._first = t._next end elseif t._prev then -- this was the last node t._prev._next = nil self._last = t._prev else -- this was the only node self._first = nil self._last = nil end t._next = nil t._prev = nil self.length = self.length - 1 end
insert
把节点插入到链表的after节点之后. 如果没指定after, 当链表里没其它节点时,仅添加这个元素。
remove 可以移除链表里的任何元素。
最后是迭代器的实现:
local function iterate(self, current) if not current then current = self.first elseif current then current = current._next end return current end function list:iterate() return iterate, self, nil end
这仅是顺序迭代,但是实现一个逆向的不难。
到结束的时候了,下面是个使用链表的例子:
require("list") local a = { 3 } local b = { 4 } local l = list({ 2 }, a, b, { 5 }) l:pop() l:shift() l:push({ 6 }) l:unshift({ 7 }) l:remove(a) l:insert({ 8 }, b) print("length", l.length) for v in l:iterate() do print(v[1]) end
如果需要全部的代码请git检出 gist #4084042.
–注,下面即是
list.lua
list = {} list.__index = list setmetatable(list, { __call = function(_, ...) local t = setmetatable({ length = 0 }, list) for _, v in ipairs{...} do t:push(v) end return t end }) function list:push(t) if self.last then self.last._next = t t._prev = self.last self.last = t else -- this is the first node self.first = t self.last = t end self.length = self.length + 1 end function list:unshift(t) if self.first then self.first._prev = t t._next = self.first self.first = t else self.first = t self.last = t end self.length = self.length + 1 end function list:insert(t, after) if after then if after._next then after._next._prev = t t._next = after._next else self.last = t end t._prev = after after._next = t self.length = self.length + 1 elseif not self.first then -- this is the first node self.first = t self.last = t end end function list:pop() if not self.last then return end local ret = self.last if ret._prev then ret._prev._next = nil self.last = ret._prev ret._prev = nil else -- this was the only node self.first = nil self.last = nil end self.length = self.length - 1 return ret end function list:shift() if not self.first then return end local ret = self.first if ret._next then ret._next._prev = nil self.first = ret._next ret._next = nil else self.first = nil self.last = nil end self.length = self.length - 1 return ret end function list:remove(t) if t._next then if t._prev then t._next._prev = t._prev t._prev._next = t._next else -- this was the first node t._next._prev = nil self._first = t._next end elseif t._prev then -- this was the last node t._prev._next = nil self._last = t._prev else -- this was the only node self._first = nil self._last = nil end t._next = nil t._prev = nil self.length = self.length - 1 end local function iterate(self, current) if not current then current = self.first elseif current then current = current._next end return current end function list:iterate() return iterate, self, nil end