48,406
edits
No edit summary |
No edit summary |
||
| Line 1: | Line 1: | ||
local export = {} | local export = {} | ||
local getmetatable = getmetatable | local getmetatable = getmetatable | ||
local gmatch = string.gmatch | |||
local ipairs = ipairs | local ipairs = ipairs | ||
local pairs = pairs | local pairs = pairs | ||
local pcall = pcall | |||
local rawequal = rawequal | |||
local rawget = rawget | |||
local select = select | local select = select | ||
local setmetatable = setmetatable | |||
local tostring = tostring | local tostring = tostring | ||
local type = type | local type = type | ||
local unpack = unpack | local unpack = unpack | ||
--[==[ | |||
Loaders for functions in other modules, which overwrite themselves with the target function when called. This ensures modules are only loaded when needed, retains the speed/convenience of locally-declared pre-loaded functions, and has no overhead after the first call, since the target functions are called directly in any subsequent calls.]= | |||
local function | local function _iterString(iter, i) | ||
i = i + 1 | |||
local char = iter() | |||
if char ~= nil then | |||
return i, char | |||
end | end | ||
end | end | ||
| Line 37: | Line 27: | ||
-- Iterate over UTF-8-encoded codepoints in string. | -- Iterate over UTF-8-encoded codepoints in string. | ||
local function iterString(str) | local function iterString(str) | ||
return _iterString, gmatch(str, ".[\128-\191]*"), 0 | |||
end | end | ||
--[==[ | --[==[ | ||
Return {true} if the input is a function or functor (a table which can be called like a function, because it has a {__call} metamethod). | Return {true} if the input is a function or functor (a table which can be called like a function, because it has a {__call} metamethod). | ||
]==] | |||
Note: if the input is a table with a protected metatable (i.e. one hidden using the `__metatable` metamethod), then this function will treat the value of `__metatable` as though it is the metatable, as that is what gets returned by `getmetatable` in such cases. If you are making use of the `__metatable` metamethod, make sure that `__metatable` is a table with a function at the `__call` key to ensure that this function returns the correct result; it does not matter if this function is the true `__call` metamethod.]==] | |||
function export.is_callable(f) | function export.is_callable(f) | ||
local f_type = type(f) | local f_type = type(f) | ||
| Line 60: | Line 41: | ||
return false | return false | ||
end | end | ||
-- A table is a functor if it has a `__call` metamethod. The only way to truly confirm this is by trying to call the table, but that could modify the table or other variables out of scope, so look for a `__call` metamethod instead. If the metatable is protected with `__metatable`, this may not be possible. | |||
local mt = getmetatable(f) | local mt = getmetatable(f) | ||
-- __call metamethods have to be functions, | if mt == nil then | ||
return | return false | ||
end | |||
-- Check if the metatable is protected: `setmetatable` will throw an error if so. | |||
local success = pcall(setmetatable, f, mt) | |||
-- If it's protected, then `mt` could be anything, but use the heuristic that if a `__call` key exists then that's probably intentional. | |||
-- This also builds in ways to ensure that this function always returns the correct result when implementing protected metatables. | |||
if not success then | |||
if type(mt) ~= "table" then | |||
return false | |||
end | |||
local __metatable = rawget(mt, "__metatable") | |||
-- If the value of `__metatable` is also `mt`, then `mt` must be the true metatable anyway (e.g. mw.loadData does this). | |||
end | |||
local __call = rawget(mt, "__call") | |||
-- `__call` metamethods have to be functions, so don't recurse when checking it. | |||
return __call ~= nil and type(__call) == "function" | |||
end | end | ||
function export.chain(func1, func2, ...) | function export.chain(func1, func2, ...) | ||
return func1(func2(...)) | return func1(func2(...)) | ||
end | |||
do | |||
local function catch_values(start, iter, state, k, ...) | |||
if start == k or k == nil then | |||
return k, ... | |||
end | |||
return catch_values(start, iter, state, iter(state, k)) | |||
end | |||
function export.iterateFrom(start, iter, state, k) | |||
local first = true | |||
return function(state, k) | |||
if first then | |||
first = false | |||
return catch_values(start, iter, state, iter(state, k)) | |||
end | |||
return iter(state, k) | |||
end, state, k | |||
end | |||
end | end | ||
| Line 75: | Line 91: | ||
-- "abc") --> { "A", "B", "C" } | -- "abc") --> { "A", "B", "C" } | ||
function export.map(func, iterable, isArray) | function export.map(func, iterable, isArray) | ||
local array = {} | local array = {} | ||
for k, v in (type(iterable) == "string" and iterString or (isArray or iterable[1] ~= nil) and ipairs or pairs)(iterable) do | |||
array[k] = func(v, k, iterable) | |||
array[ | |||
end | end | ||
return array | return array | ||
end | end | ||
function export.mapIter(func, iter, | function export.mapIter(func, iter, state, init) | ||
-- init could be anything | |||
local array, i = {}, 0 | |||
for x, y in iter, state, init do | |||
-- | |||
local array = {} | |||
for x, y in iter, | |||
i = i + 1 | i = i + 1 | ||
array[i] = func(y, x, | array[i] = func(y, x, state) | ||
end | end | ||
return array | return array | ||
end | |||
do | |||
local function iter_tuples(tuples) | |||
local i = tuples.i | |||
if i > 1 then | |||
i = i - 1 | |||
tuples.i = i | |||
return unpack(tuples[i]) | |||
end | |||
end | |||
-- Takes an iterator function, and returns a new iterator that iterates in reverse, given the same arguments. | |||
-- Note: changes to the state during iteration are not taken into account, since all the return values are calculated in advance. | |||
function export.reverseIter(func) | |||
return function(...) | |||
-- Store all returned values as a list of tuples, then iterate in reverse over that list. | |||
local tuples, i, iter, state, val1 = {}, 0, func(...) | |||
while true do | |||
i = i + 1 | |||
local vals = {iter(state, val1)} | |||
-- Terminates if the first return value is nil, even if other values are non-nil. | |||
val1 = vals[1] | |||
if val1 == nil then | |||
tuples.i = i | |||
return iter_tuples, tuples | |||
end | |||
tuples[i] = vals | |||
end | |||
end | |||
end | |||
end | end | ||
function export.forEach(func, iterable, isArray) | function export.forEach(func, iterable, isArray) | ||
for k, v in (type(iterable) == "string" and iterString or (isArray or iterable[1] ~= nil) and ipairs or pairs)(iterable) do | |||
func(v, k, iterable) | |||
func( | |||
end | end | ||
return nil | return nil | ||
| Line 126: | Line 154: | ||
-- reverse args by building a function to do it, similar to the unpack() example | -- reverse args by building a function to do it, similar to the unpack() example | ||
local function reverseHelper(acc, v, ...) | local function reverseHelper(acc, v, ...) | ||
if select( | if select("#", ...) == 0 then | ||
return v, acc() | return v, acc() | ||
else | else | ||
| Line 166: | Line 194: | ||
-- { 2, 3, 5, 7, 11 }) --> true | -- { 2, 3, 5, 7, 11 }) --> true | ||
function export.some(func, t, isArray) | function export.some(func, t, isArray) | ||
for k, v in ((isArray or t[1] ~= nil) and ipairs or pairs)(t) do | |||
if func(v, k, t) then | |||
return true | |||
end | end | ||
end | end | ||
| Line 185: | Line 205: | ||
-- { 2, 4, 8, 10, 12 }) --> true | -- { 2, 4, 8, 10, 12 }) --> true | ||
function export.all(func, t, isArray) | function export.all(func, t, isArray) | ||
for k, v in ((isArray or t[1] ~= nil) and ipairs or pairs)(t) do | |||
if not func(v, k, t) then | |||
return false | |||
end | end | ||
end | end | ||
| Line 232: | Line 244: | ||
-- Fancy stuff | -- Fancy stuff | ||
local function capture(...) | local function capture(...) | ||
local vals = { n = select( | local vals = {n = select("#", ...), ...} | ||
return function() | return function() | ||
return unpack(vals, 1, vals.n) | return unpack(vals, 1, vals.n) | ||
| Line 265: | Line 277: | ||
end | end | ||
return t | return t | ||
end | end | ||
return export | return export | ||