Advertisement
Snusmumriken

Lua array lib with functional programming

Jan 26th, 2017
318
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.43 KB | None | 0 0
  1. local default = function(a, b) return a < b end
  2.  
  3. local ch = {n = 'number', s = 'string', t = 'table', f = 'function', c = 'cdata'}
  4. local errorstr = 'Array: arg#%d error: "%s" expected, got "%s" '
  5. local function checker(...)
  6.     local list, ch, a, b = {...}, ch
  7.     for i = 1, math.floor(#list/2) do
  8.         a, b = list[i*2-1], list[i*2]
  9.         a = a and type(a) == ch[b:sub(1, 1)]
  10.               or b:sub(-2) == '?' and type(a) == 'nil'
  11.               or error(errorstr:format(i, ch[b:sub(1, 1)], type(a)), 3)
  12.     end
  13. end
  14.  
  15. -- binary search in (sorted) array with optional compare-function
  16. local function binsearch(array, value, fcomp)
  17.     local floor = math.floor
  18.     local i, j = 1, #array
  19.     fcomp = fcomp or default
  20.  
  21.     -- values iteration limiter
  22.     while j - i > 10 do
  23.         local mid = floor((j - i) * .5)
  24.         if fcomp(array[i + mid], value) then
  25.             i = i + mid
  26.         else
  27.             j = j - mid + 1
  28.         end
  29.     end
  30.     for i = i, j do
  31.         if not fcomp(array[i], value) then return i end
  32.     end
  33.     return #array
  34. end
  35.  
  36. local function binsert(array, value, fcomp)
  37.     local i = binsearch(array, value, fcomp)
  38.     table.insert(array, i, value)
  39. end
  40.  
  41. local function gnomeSort(t, fcomp)
  42.     local fcomp = fcomp or default
  43.     local i, j = 2, 3
  44.     while i < #t do
  45.         if fcomp(t[i-1], t[i]) then
  46.             i, j = j, j + 1
  47.         else
  48.             t[i-1], t[i] = t[i], t[i-1]
  49.             i = i - 1
  50.             if i == 1 then
  51.                 i, j = j, j + 1
  52.             end
  53.         end
  54.     end
  55. end
  56.  
  57. local function round(v) return math.floor(math.ceil(v - 0.5)) end
  58. local function combsort(t, fcomp)
  59.     local fcomp, round = fcomp or default, round
  60.     local f, len = round(#t*0.80171245780988), #t
  61.     while f >=1 do
  62.         local i = 1
  63.         while i + f < len do
  64.             if not fcomp(t[i], t[i+f]) then
  65.                 t[i], t[i+f] = t[i+f], t[i]
  66.             end
  67.             i = i + 1
  68.         end
  69.         f = f - 1
  70.     end
  71. end
  72.  
  73. local array = setmetatable({}, {__index = table,
  74.     __call = function(self, t)
  75.         return setmetatable(type(t) == 'table' and t or {}, self)
  76.     end
  77. })
  78. array.__index = array
  79. array.__tostring = function(self) return '['..self:concat(', ')..']' end
  80. array.__concat = function(a, b) return tostring(a)..tostring(b) end
  81.  
  82. array.ipairs = ipairs
  83. array.pairs = pairs
  84. array.print = print
  85. array.unpack = unpack
  86. function array:ripairs()
  87.     local i = #self + 1
  88.     return function()
  89.         i = i - 1;
  90.         if self[i] then return i, self[i], nil end
  91.     end
  92. end
  93.  
  94. function array:print(delm) print('['..self:concat(delm or ', ')..']') end
  95. function array:sort(f) checker(self, 't', f, 'f?') table.sort(self, f); return self end
  96.  
  97. local function slice(t, a, b)
  98.     a = a or 1; b = b or #t
  99.     local l = #t
  100.     a = a > 0 and a or #t+a+1; a = a < 0 and 0 or a > l and l or a
  101.     b = b > 0 and b or #t+b+1; b = b < 0 and 0 or b > l and l or b
  102.     return a, b
  103. end
  104.  
  105. local function range(a, b, c)
  106.     checker(a, 'n', b, 'n?', c, 'n?')
  107.     local t = array()
  108.     a, b = a and b and a or 0, b and b or a
  109.     c = c or a < b and 1 or -1
  110.     for i = a, b - c, c do t:insert(i) end
  111.     return t
  112. end
  113.  
  114. -- like average 'for', but iterator
  115. local function xrange(a, b, c)
  116.     checker(a, 'n', b, 'n?', c, 'n?')
  117.     a, b = a and b and a or 0, b and b or a
  118.     c = c or a < b and 1 or -1
  119.     b = b - c
  120.     local i = a - c
  121.     return function()
  122.         i = i + c
  123.         if i < b then
  124.             return i, nil
  125.         end
  126.     end
  127. end
  128.  
  129. -- slice array between one or two values
  130. function array:slice(a, b)
  131.     checker(self, 't', a, 'n?', b, 'n?')
  132.     local t, i, j = array(), slice(self, a, b)
  133.     for i = i, j do t:insert(self[i]) end
  134.     return t
  135. end
  136.  
  137. -- slice iterator
  138. function array:islice(a, b)
  139.     checker(self, 't', a, 'n?', b, 'n?')
  140.     local i, j = slice(self, a, b); i = i - 1
  141.     return function()
  142.         i = i + 1; if i <= j then return i, self[i], nil end
  143.     end
  144. end
  145.  
  146. function array:map(f)
  147.     checker(self, 't', f, 'f')
  148.     for i = 1, #self do self[i] = f(self[i]) end
  149.     return self
  150. end
  151.  
  152. -- function to zip/map some tables/arrays like this:
  153. -- arr = map(func(x, y, z, ...) return x * y * z end, arr1, arr2, arr3, ...)
  154. -- where x, y and z is iterated array values. Iteration processed by shortest array.
  155. local function map(f, ...)
  156.     checker(f, 'f')
  157.     local list, t, len = array{...}, array(), 1
  158.     for i = 1, #list do
  159.         local v, l = list[i], #list[i]
  160.         if type(v) ~= 'table' then error(errorstr:format(i+1, 'table', type(v)), 2) end
  161.         len = len < l and l or len
  162.     end
  163.     if #list == 1 then      -- shortcut, faster then a lot of arrays
  164.         for i = 1, len do t:insert(f(list[1][i])) end
  165.     else
  166.         for i = 1, len do
  167.             local comb = array()
  168.             for j = 1, #list do comb:insert(list[j][i]) end
  169.             t:insert(f(comb:unpack()))
  170.         end
  171.     end
  172.     return t
  173. end
  174.  
  175. function array:filter(f)
  176.     checker(self, 't', f, 'f')
  177.     local t = array()
  178.     for i = 1, #self do
  179.         if f(self[i]) then t:insert(self[i]) end
  180.     end
  181.     return t
  182. end
  183.  
  184. function array:reduce(f)
  185.     checker(self, 't', f, 'f')
  186.     local res = array()
  187.     for i = 1, #self do f(self[i], res) end
  188.     return res
  189. end
  190.  
  191. function array:range(a, b, c)
  192.     checker(self, 't', a, 'n?', b, 'n?', c, 'n?')
  193.     local len = #self
  194.     a, b = a and b and a or 1, b or a
  195.     a = a > len and len or a < 1 and 1 or a
  196.     b = b > len and len or b < 1 and 1 or b
  197.     local c, t = math.floor(c) or a < b and 1 or -1, array()
  198.     for i = a, b, c do t:insert(self[i]) end
  199.     return t
  200. end
  201.  
  202. -- arrow function implementation
  203. -- using like array.f'foo, bar => foo * bar'
  204. local template = 'return function(%s) return %s end'
  205. local buffer = setmetatable({}, {__mode = 'kv'})
  206. local function l(expression)
  207.     if buffer[expression] then return buffer[expression] end
  208.     buffer[expression] = loadstring(template:format(expression:match('(.-)=>(.*)')))()
  209.     return buffer[expression]
  210. end
  211.  
  212. if not ... then
  213.     -- unit testing and reference
  214.     -- array has methods from standart 'table' library, like arr:insert(v, pos) or arr:sort()
  215.     range(10, -20, -0.25)                             -- return array in range (start value, end value, step)
  216.         :map(l'x => x + 10')                          -- apply funcion to every value in array
  217.         :reduce(l'x, res => res:insert(x*2)')         -- apply funcion to every value in array, res - store-table
  218.         :sort(l'a, b => a > b')                       -- sorting (average)
  219.         :filter(l'x => x > 0')                        -- filter values by bool function
  220.         :slice(-20)                                   -- slice values by i, j, values can be less then zero
  221.         :print()                                      -- just debug printing
  222.  
  223. else return {
  224.         binsearch = binsearch,
  225.         binsert = binsert,
  226.         sort = setmetatable({gnome = gnomeSort, comb = combsort}, {__call = table.sort}),
  227.         array = array,
  228.         range = range,
  229.         xrange = xrange,
  230.         map = map,
  231.         f = l
  232.     }
  233. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement