Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local default = function(a, b) return a < b end
- local ch = {n = 'number', s = 'string', t = 'table', f = 'function', c = 'cdata'}
- local errorstr = 'Array: arg#%d error: "%s" expected, got "%s" '
- local function checker(...)
- local list, ch, a, b = {...}, ch
- for i = 1, math.floor(#list/2) do
- a, b = list[i*2-1], list[i*2]
- a = a and type(a) == ch[b:sub(1, 1)]
- or b:sub(-2) == '?' and type(a) == 'nil'
- or error(errorstr:format(i, ch[b:sub(1, 1)], type(a)), 3)
- end
- end
- -- binary search in (sorted) array with optional compare-function
- local function binsearch(array, value, fcomp)
- local floor = math.floor
- local i, j = 1, #array
- fcomp = fcomp or default
- -- values iteration limiter
- while j - i > 10 do
- local mid = floor((j - i) * .5)
- if fcomp(array[i + mid], value) then
- i = i + mid
- else
- j = j - mid + 1
- end
- end
- for i = i, j do
- if not fcomp(array[i], value) then return i end
- end
- return #array
- end
- local function binsert(array, value, fcomp)
- local i = binsearch(array, value, fcomp)
- table.insert(array, i, value)
- end
- local function gnomeSort(t, fcomp)
- local fcomp = fcomp or default
- local i, j = 2, 3
- while i < #t do
- if fcomp(t[i-1], t[i]) then
- i, j = j, j + 1
- else
- t[i-1], t[i] = t[i], t[i-1]
- i = i - 1
- if i == 1 then
- i, j = j, j + 1
- end
- end
- end
- end
- local function round(v) return math.floor(math.ceil(v - 0.5)) end
- local function combsort(t, fcomp)
- local fcomp, round = fcomp or default, round
- local f, len = round(#t*0.80171245780988), #t
- while f >=1 do
- local i = 1
- while i + f < len do
- if not fcomp(t[i], t[i+f]) then
- t[i], t[i+f] = t[i+f], t[i]
- end
- i = i + 1
- end
- f = f - 1
- end
- end
- local array = setmetatable({}, {__index = table,
- __call = function(self, t)
- return setmetatable(type(t) == 'table' and t or {}, self)
- end
- })
- array.__index = array
- array.__tostring = function(self) return '['..self:concat(', ')..']' end
- array.__concat = function(a, b) return tostring(a)..tostring(b) end
- array.ipairs = ipairs
- array.pairs = pairs
- array.print = print
- array.unpack = unpack
- function array:ripairs()
- local i = #self + 1
- return function()
- i = i - 1;
- if self[i] then return i, self[i], nil end
- end
- end
- function array:print(delm) print('['..self:concat(delm or ', ')..']') end
- function array:sort(f) checker(self, 't', f, 'f?') table.sort(self, f); return self end
- local function slice(t, a, b)
- a = a or 1; b = b or #t
- local l = #t
- a = a > 0 and a or #t+a+1; a = a < 0 and 0 or a > l and l or a
- b = b > 0 and b or #t+b+1; b = b < 0 and 0 or b > l and l or b
- return a, b
- end
- local function range(a, b, c)
- checker(a, 'n', b, 'n?', c, 'n?')
- local t = array()
- a, b = a and b and a or 0, b and b or a
- c = c or a < b and 1 or -1
- for i = a, b - c, c do t:insert(i) end
- return t
- end
- -- like average 'for', but iterator
- local function xrange(a, b, c)
- checker(a, 'n', b, 'n?', c, 'n?')
- a, b = a and b and a or 0, b and b or a
- c = c or a < b and 1 or -1
- b = b - c
- local i = a - c
- return function()
- i = i + c
- if i < b then
- return i, nil
- end
- end
- end
- -- slice array between one or two values
- function array:slice(a, b)
- checker(self, 't', a, 'n?', b, 'n?')
- local t, i, j = array(), slice(self, a, b)
- for i = i, j do t:insert(self[i]) end
- return t
- end
- -- slice iterator
- function array:islice(a, b)
- checker(self, 't', a, 'n?', b, 'n?')
- local i, j = slice(self, a, b); i = i - 1
- return function()
- i = i + 1; if i <= j then return i, self[i], nil end
- end
- end
- function array:map(f)
- checker(self, 't', f, 'f')
- for i = 1, #self do self[i] = f(self[i]) end
- return self
- end
- -- function to zip/map some tables/arrays like this:
- -- arr = map(func(x, y, z, ...) return x * y * z end, arr1, arr2, arr3, ...)
- -- where x, y and z is iterated array values. Iteration processed by shortest array.
- local function map(f, ...)
- checker(f, 'f')
- local list, t, len = array{...}, array(), 1
- for i = 1, #list do
- local v, l = list[i], #list[i]
- if type(v) ~= 'table' then error(errorstr:format(i+1, 'table', type(v)), 2) end
- len = len < l and l or len
- end
- if #list == 1 then -- shortcut, faster then a lot of arrays
- for i = 1, len do t:insert(f(list[1][i])) end
- else
- for i = 1, len do
- local comb = array()
- for j = 1, #list do comb:insert(list[j][i]) end
- t:insert(f(comb:unpack()))
- end
- end
- return t
- end
- function array:filter(f)
- checker(self, 't', f, 'f')
- local t = array()
- for i = 1, #self do
- if f(self[i]) then t:insert(self[i]) end
- end
- return t
- end
- function array:reduce(f)
- checker(self, 't', f, 'f')
- local res = array()
- for i = 1, #self do f(self[i], res) end
- return res
- end
- function array:range(a, b, c)
- checker(self, 't', a, 'n?', b, 'n?', c, 'n?')
- local len = #self
- a, b = a and b and a or 1, b or a
- a = a > len and len or a < 1 and 1 or a
- b = b > len and len or b < 1 and 1 or b
- local c, t = math.floor(c) or a < b and 1 or -1, array()
- for i = a, b, c do t:insert(self[i]) end
- return t
- end
- -- arrow function implementation
- -- using like array.f'foo, bar => foo * bar'
- local template = 'return function(%s) return %s end'
- local buffer = setmetatable({}, {__mode = 'kv'})
- local function l(expression)
- if buffer[expression] then return buffer[expression] end
- buffer[expression] = loadstring(template:format(expression:match('(.-)=>(.*)')))()
- return buffer[expression]
- end
- if not ... then
- -- unit testing and reference
- -- array has methods from standart 'table' library, like arr:insert(v, pos) or arr:sort()
- range(10, -20, -0.25) -- return array in range (start value, end value, step)
- :map(l'x => x + 10') -- apply funcion to every value in array
- :reduce(l'x, res => res:insert(x*2)') -- apply funcion to every value in array, res - store-table
- :sort(l'a, b => a > b') -- sorting (average)
- :filter(l'x => x > 0') -- filter values by bool function
- :slice(-20) -- slice values by i, j, values can be less then zero
- :print() -- just debug printing
- else return {
- binsearch = binsearch,
- binsert = binsert,
- sort = setmetatable({gnome = gnomeSort, comb = combsort}, {__call = table.sort}),
- array = array,
- range = range,
- xrange = xrange,
- map = map,
- f = l
- }
- end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement