LiquidProxy Lua Edition
at master 405 lines 9.0 kB view raw
1local table = {} 2for k,v in pairs(require 'table') do table[k] = v end 3 4table.__index = table 5 6function table.new(...) 7 return setmetatable({}, table):union(...) 8end 9 10setmetatable(table, { 11 __call = function(t, ...) 12 return table.new(...) 13 end 14}) 15 16-- 5.2 or 5.3 compatible 17table.unpack = table.unpack or unpack 18 19-- [[ how about table.unpack(t) defaults to table.unpack(t, 1, t.n) if t.n is present? 20-- for cohesion with table.pack? 21-- already table.unpack's default is #t, but this doesn't account for nils 22-- this might break compatability somewhere ... 23local origTableUnpack = table.unpack 24function table.unpack(...) 25 local nargs = select('#', ...) 26 local t, i, j = ... 27 if nargs < 3 and t.n ~= nil then 28 return origTableUnpack(t, i or 1, t.n) 29 end 30 return origTableUnpack(...) 31end 32--]] 33 34-- 5.1 compatible 35if not table.pack then 36 function table.pack(...) 37 local t = {...} 38 t.n = select('#', ...) 39 return setmetatable(t, table) 40 end 41else 42 local oldpack = table.pack 43 function table.pack(...) 44 return setmetatable(oldpack(...), table) 45 end 46end 47 48-- non-5.1 compat: 49if not table.maxn then 50 function table.maxn(t) 51 local max = 0 52 for k,v in pairs(t) do 53 if type(k) == 'number' then 54 max = math.max(max, k) 55 end 56 end 57 return max 58 end 59end 60 61-- applies to the 'self' table 62-- same behavior as new 63function table:union(...) 64 for i=1,select('#', ...) do 65 local o = select(i, ...) 66 if o then 67 for k,v in pairs(o) do 68 self[k] = v 69 end 70 end 71 end 72 return self 73end 74 75-- something to consider: 76-- mapvalue() returns a new table 77-- but append() modifies the current table 78-- for consistency shouldn't append() create a new one as well? 79function table:append(...) 80 for i=1,select('#', ...) do 81 local u = select(i, ...) 82 if u then 83 for _,v in ipairs(u) do 84 table.insert(self, v) 85 end 86 end 87 end 88 return self 89end 90 91function table:removeKeys(...) 92 for i=1,select('#', ...) do 93 local v = select(i, ...) 94 self[v] = nil 95 end 96end 97 98-- cb(value, key, newtable) returns newvalue[, newkey] 99-- nil newkey means use the old key 100function table:map(cb) 101 local t = table() 102 for k,v in pairs(self) do 103 local nv, nk = cb(v,k,t) 104 if nk == nil then nk = k end 105 t[nk] = nv 106 end 107 return t 108end 109 110-- cb(value, key, newtable) returns newvalue[, newkey] 111-- nil newkey means use the old key 112function table:mapi(cb) 113 local t = table() 114 for k=1,#self do 115 local v = self[k] 116 local nv, nk = cb(v,k,t) 117 if nk == nil then nk = k end 118 t[nk] = nv 119 end 120 return t 121end 122 123-- this excludes keys that don't pass the callback function 124-- if the key is an ineteger then it is table.remove'd 125-- currently the handling of integer keys is the only difference between this 126-- and calling table.map and returning nil kills on filtered items 127function table:filter(f) 128 local t = table() 129 for k,v in pairs(self) do 130 if f(v,k) then 131 -- TODO now that i made filteri, should this only ever always directly map keys 132 -- even if the key is an integer? 133 -- or should it still insert integer keys? 134 if type(k) == 'string' then 135 t[k] = v 136 else 137 t:insert(v) 138 end 139 end 140 end 141 return t 142end 143 144-- like filter but only works on ipairs entries 145function table:filteri(f) 146 local t = table() 147 for k,v in ipairs(self) do 148 if f(v,k) then 149 t:insert(v) 150 end 151 end 152 return t 153end 154 155function table:keys() 156 local t = table() 157 for k,_ in pairs(self) do 158 t:insert(k) 159 end 160 return t 161end 162 163function table:values() 164 local t = table() 165 for _,v in pairs(self) do 166 t:insert(v) 167 end 168 return t 169end 170 171-- should we also return value, key to match map, sup, and inf? 172-- that seems redundant if it's find-by-value ... 173function table:find(value, eq) 174 if eq then 175 for k,v in pairs(self) do 176 if eq(v, value) then return k, v end 177 end 178 else 179 for k,v in pairs(self) do 180 if v == value then return k, v end 181 end 182 end 183end 184 185function table:findi(value, eq) 186 if eq then 187 for k,v in ipairs(self) do 188 if eq(v, value) then return k, v end 189 end 190 else 191 for k,v in ipairs(self) do 192 if v == value then return k, v end 193 end 194 end 195end 196 197-- should insertUnique only operate on the pairs() ? 198-- especially when insert() itself is an ipairs() operation 199function table:insertUnique(value, eq) 200 if not table.find(self, value, eq) then table.insert(self, value) end 201end 202 203function table:removeObject(...) 204 local removedKeys = table() 205 local len = #self 206 local k = table.find(self, ...) 207 while k ~= nil do 208 if type(k) == 'number' and tonumber(k) <= len then 209 table.remove(self, k) 210 else 211 self[k] = nil 212 end 213 removedKeys:insert(k) 214 k = table.find(self, ...) 215 end 216 return table.unpack(removedKeys) 217end 218 219function table:kvpairs() 220 local t = table() 221 for k,v in pairs(self) do 222 table.insert(t, {[k]=v}) 223 end 224 return t 225end 226 227-- TODO - math instead of table? 228-- TODO - have cmp default to operator> just like inf and sort? 229function table:sup(cmp) 230 local bestk, bestv 231 if cmp then 232 for k,v in pairs(self) do 233 if bestv == nil or cmp(v, bestv) then bestk, bestv = k, v end 234 end 235 else 236 for k,v in pairs(self) do 237 if bestv == nil or v > bestv then bestk, bestv = k, v end 238 end 239 end 240 return bestv, bestk 241end 242 243-- TODO - math instead of table? 244function table:inf(cmp) 245 local bestk, bestv 246 if cmp then 247 for k,v in pairs(self) do 248 if bestv == nil or cmp(v, bestv) then bestk, bestv = k, v end 249 end 250 else 251 for k,v in pairs(self) do 252 if bestv == nil or v < bestv then bestk, bestv = k, v end 253 end 254 end 255 return bestv, bestk 256end 257 258-- combine elements of 259function table:combine(callback) 260 local s 261 for _,v in pairs(self) do 262 if s == nil then 263 s = v 264 else 265 s = callback(s, v) 266 end 267 end 268 return s 269end 270 271local op = require 'ext.op' 272 273function table:sum() 274 return table.combine(self, op.add) 275end 276 277function table:product() 278 return table.combine(self, op.mul) 279end 280 281function table:last() 282 return self[#self] 283end 284 285-- just like string subset 286function table.sub(t,i,j) 287 if i < 0 then i = math.max(1, #t + i + 1) end 288 --if i < 0 then i = math.max(1, #t + i + 1) else i = math.max(1, i) end -- TODO this is affecting symmath edge cases somewhere ... 289 j = j or #t 290 j = math.min(j, #t) 291 if j < 0 then j = math.min(#t, #t + j + 1) end 292 --if j < 0 then j = math.min(#t, #t + j + 1) else j = math.max(1, j) end -- TODO this is affecting symmath edge cases somewhere ... 293 local res = {} 294 for k=i,j do 295 res[k-i+1] = t[k] 296 end 297 setmetatable(res, table) 298 return res 299end 300 301function table.reverse(t) 302 local r = table() 303 for i=#t,1,-1 do 304 r:insert(t[i]) 305 end 306 return r 307end 308 309function table.rep(t,n) 310 local c = table() 311 for i=1,n do 312 c:append(t) 313 end 314 return c 315end 316 317-- in-place sort is fine, but it returns nothing. for kicks I'd like to chain methods 318local oldsort = require 'table'.sort 319function table:sort(...) 320 oldsort(self, ...) 321 return self 322end 323 324-- returns a shuffled duplicate of the ipairs in table 't' 325function table.shuffle(t) 326 t = table(t) 327 -- https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle 328 for i=#t,2,-1 do 329 local j = math.random(i-1) 330 t[i], t[j] = t[j], t[i] 331 end 332 return t 333end 334 335function table.pickRandom(t) 336 return t[math.random(#t)] 337end 338 339-- keys = options, values = probs 340function table.pickWeighted(t) 341 local total = table.values(t):sum() 342 local x = math.random() * total 343 for k,v in pairs(t) do 344 x = x - v 345 if x <= 0 then 346 return k 347 end 348 end 349 -- this should never be reached unless you have some not-a-number's as values 350end 351 352-- where to put this ... 353-- I want to convert iterators into tables 354-- it looks like a coroutine but it is made for functions returned from coroutine.wrap 355-- also, what to do with multiple-value iterators (like ipairs) 356-- do I only wrap the first value? 357-- do I wrap both values in a double table? 358-- do I do it optionally based on the # args returned? 359-- how about I ask for a function to convert the iterator to the table? 360-- this is looking very similar to table.map 361-- I'll just wrap it with table.wrap and then let the caller use :mapi to transform the results 362-- usage: table.wrapfor(ipairs(t)) 363-- if you want to wrap a 'for=' loop then just use range(a,b[,c]) 364-- ok at this point I should just start using lua-fun ... 365function table.wrapfor(f, s, var) 366 local t = table() 367 while true do 368 local vars = table.pack(f(s, var)) 369 local var_1 = vars[1] 370 if var_1 == nil then break end 371 var = var_1 372 t:insert(vars) 373 end 374 return t 375end 376 377-- https://www.lua.org/pil/9.3.html 378local function permgen(t, n) 379 if n < 1 then 380 coroutine.yield(t) 381 else 382 for i=n,1,-1 do 383 -- put i-th element as the last one 384 t[n], t[i] = t[i], t[n] 385 -- generate all permutations of the other elements 386 permgen(t, n - 1) 387 -- restore i-th element 388 t[n], t[i] = t[i], t[n] 389 end 390 end 391end 392 393-- return iterator of permutations of the table 394function table.permutations(t) 395 return coroutine.wrap(function() 396 permgen(t, table.maxn(t)) 397 end) 398end 399 400-- I won't add table.getmetatable because, as a member method, that will always return 'table' 401 402-- if you use this as a member method then know that you can't use it a second time (unless the metatable you set it to has a __index that has 'setmetatable' defined) 403table.setmetatable = setmetatable 404 405return table