PageRenderTime 137ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/src/list.lua

http://github.com/daogangtang/lglib
Lua | 300 lines | 210 code | 49 blank | 41 comment | 43 complexity | a294ad174729eb9c2639bc8f2c1e4971 MD5 | raw file
  1. local string, table = string, table
  2. local tinsert, tremove, concat, tsort = table.insert, table.remove, table.concat, table.sort
  3. local assert = assert
  4. local equal = equal
  5. module(..., package.seeall)
  6. -- this is a LIST prototype, and all of list instances inherit it
  7. local List = {}
  8. local List_meta = {}
  9. List_meta.__index = List
  10. List_meta.__typename = "list"
  11. List_meta.__newindex = function (self, k, v)
  12. assert(type(k) == 'number', "[Error] List can only accept number as index.")
  13. assert(k > 0 and k <= #self + 1 , "[Error] index overflow.")
  14. rawset(self, k, v)
  15. end
  16. -- constructor of List object
  17. local function new (tbl)
  18. tbl = tbl or {}
  19. assert(type(tbl) == 'table', "[Error] paramerter passed in List constructor should be table.")
  20. -- here, we keep the thing simple to keep speed
  21. -- setting the inheritance relationship
  22. return setmetatable(tbl, List_meta)
  23. end
  24. -- binding constructor new(tbl) with List() sytanx
  25. -- table can be accessed via __index from its/List metatable. It means List can reuse the table API??
  26. setmetatable(List, {
  27. __call = function (self, tbl)
  28. return new(tbl)
  29. end,
  30. })
  31. -- the normalization of indice
  32. function normalize_slice( self, start, stop )
  33. local start = start or 1
  34. local stop = stop or #self
  35. if stop > 0 and start > 0 or stop < 0 and start < 0 then assert(stop >= start) end
  36. if start > #self then return nil, nil end
  37. -- the negative index
  38. -- -1 is the last elment, -2 the penultimate, and so on
  39. if start == 0 then
  40. start = 1
  41. elseif start < 0 then
  42. if math.abs(start) >= #self then
  43. start = 1
  44. else
  45. start = #self + start + 1
  46. end
  47. end
  48. if stop == 0 then
  49. stop = 1
  50. elseif stop < 0 then
  51. stop = #self + stop + 1
  52. if stop < 1 then return nil, nil end
  53. end
  54. return start, stop
  55. end
  56. ------------------------------------------------------------------------
  57. -- @class method
  58. -- start para is optional, and the default value is 1
  59. -- generate a sequence of integers
  60. function List.range(start, finish)
  61. if not finish then
  62. finish = start
  63. start = 1
  64. end
  65. checkType(start, finish, 'number', 'number')
  66. local t = new()
  67. for i = start, finish do tinsert(t, i) end
  68. return t
  69. end
  70. ------------------------------------------------------------------------
  71. -- @object methods
  72. -- appending extra element at the tail of list
  73. function List:append(val)
  74. tinsert(self, val)
  75. return self
  76. end
  77. -- super slow, time complexity is O(N). IF implemented by lua-table, it will be much better
  78. function List:prepend(val)
  79. tinsert(self, 1, val)
  80. return self
  81. end
  82. --list expansion by another one
  83. function List:extend( another )
  84. checkType(another, 'list')
  85. for i = 1, #another do tinsert(self, another[i]) end
  86. return self
  87. end
  88. List.extends = List.extend
  89. -- delete by index
  90. function List:pop (i)
  91. tremove(self, i)
  92. return self
  93. end
  94. -- delete by value, and all of them
  95. -- maybe a better API is List:remove(x, numOfDeletion)
  96. -- if numOfDeletion is negative integer,then counting from the last one in reversing order.
  97. function List:remove(x)
  98. for i = #self, 1, -1 do
  99. if self[i] == x then tremove(self, i) end
  100. end
  101. return self
  102. end
  103. -- push a new element into list at right-hand side
  104. List.push = List.append
  105. -- starting from idx index and trying to find the first element with value=val,
  106. function List:find(val, idx)
  107. local idx = idx or 1
  108. if idx < 0 then idx = #self + idx + 1 end
  109. for i = idx, #self do
  110. if self[i] == val then return i end
  111. end
  112. return nil
  113. end
  114. -- contain element x or not
  115. function List:contains(x)
  116. return self:find(x, 1) and true or false
  117. end
  118. -- counting the times that element x appears in a list
  119. function List:count(x)
  120. local cnt=0
  121. for i=1, #self do
  122. if self[i] == x then cnt = cnt+1 end
  123. end
  124. return cnt
  125. end
  126. -- simple wrapper of table.concat() method
  127. function List:join(sep)
  128. local _t = {}
  129. for _, v in ipairs(self) do
  130. tinsert(_t, tostring(v))
  131. end
  132. return concat(_t, sep)
  133. end
  134. -- sorting, a simple wrapper of table.sort()
  135. function List:sort(cmp)
  136. tsort(self, cmp)
  137. return self
  138. end
  139. -- reverse the order of list elements
  140. function List:reverse()
  141. local t = self
  142. local n = #t
  143. local n2 = n/2
  144. for i = 1, n2 do
  145. local k = n - i + 1
  146. t[i], t[k] = t[k], t[i]
  147. end
  148. return self
  149. end
  150. -- slicing
  151. -- start, stop maybe nil, negative integer, or other values
  152. function List:slice(start, stop, is_rev)
  153. -- NOTICE: here, should not use checkType!
  154. -- because start, stop, is_rev are all probably nil.
  155. local self_meta = getmetatable(self)
  156. local nt = setmetatable({}, self_meta)
  157. local start, stop = normalize_slice(self, start, stop)
  158. if not start or not stop then return nt end
  159. if is_rev ~= 'rev' then
  160. for i = start, (#self > stop and stop or #self) do
  161. tinsert(nt, self[i])
  162. end
  163. else
  164. for i = (#self > stop and stop or #self), start, -1 do
  165. tinsert(nt, self[i])
  166. end
  167. end
  168. return nt
  169. end
  170. -- delete all of list elements
  171. function List:clear()
  172. for i=1, #self do tremove(self, i) end
  173. return self
  174. end
  175. -- length/size of list
  176. function List:len()
  177. return #self
  178. end
  179. -- deleted by indexing interval
  180. function List:chop(i1,i2)
  181. local i1, i2 = normalize_slice(i1, i2)
  182. for i = i1, i2 do
  183. tremove(t, i)
  184. end
  185. return self
  186. end
  187. -- insert another *list* at the location *idx*
  188. function List:splice(idx, list)
  189. checkType(idx, list, 'number', 'list')
  190. local i = idx
  191. for _, v in ipairs(list) do
  192. tinsert(self, i, v)
  193. i = i + 1
  194. end
  195. return self
  196. end
  197. -- assignment in the style of slicing
  198. function List:sliceAssign(i1, i2, seq)
  199. checkType(i1, i2, 'number', 'number')
  200. local i1, i2 = normalize_slice(self, i1, i2)
  201. local delta = i2 - i1 + 1
  202. -- old implementation
  203. if i2 >= i1 then self:chop(i1, i2) end
  204. self:splice(i1, seq)
  205. -- new implementation
  206. for i = 1, delta do
  207. self[i1 + i - 1] = seq[i]
  208. end
  209. return self
  210. end
  211. function List_meta:__add(another)
  212. checkType(another, 'list')
  213. ls:extend(another)
  214. return ls
  215. end
  216. List.__eq = equal
  217. function List_meta:__tostring()
  218. return '[' .. self:join(', ') .. ']'
  219. end
  220. function List:each(func)
  221. local ret = List()
  222. if self then
  223. for _, v in ipairs(self) do
  224. ret:append(func(v))
  225. end
  226. end
  227. return ret
  228. end
  229. function List:dump(count)
  230. for i=1, #self do
  231. if type(self[i]) == 'string' then
  232. print(i, self[i])
  233. else
  234. if self[i].dump then
  235. self[i]:dump(count)
  236. end
  237. end
  238. end
  239. end
  240. --
  241. function List:isEmpty ()
  242. if type(self) ~= 'table' then
  243. error('You use isEmpty(), but the parameter is not a list.', 2)
  244. end
  245. if #self == 0 then
  246. return true
  247. else
  248. return false
  249. end
  250. end
  251. return List