Jump to content

Module:User:Erutuon/functional

From Wiktionary, the free dictionary

Β (2, 1) → β, Γ (4, 2) → γ, Δ (6, 3) → δ, Ζ (8, 4) → ζ, Θ (10, 5) → θ, Κ (12, 6) → κ, Λ (14, 7) → λ, Μ (16, 8) → μ, Ν (18, 9) → ν, Ξ (20, 10) → ξ, Π (22, 11) → π, Ρ (24, 12) → ρ, Σ (26, 13) → σ, Σ (28, 14) → σ, Τ (30, 15) → τ, Φ (32, 16) → φ, Χ (34, 17) → χ, Ψ (36, 18) → ψ


local export = {}

local ustring = mw.ustring
local libraryUtil = require "libraryUtil"
local checkType = libraryUtil.checkType
local checkTypeMulti = libraryUtil.checkTypeMulti

local iterableTypes = { "table", "string" }

local function _check(funcName, expectType)
	if type(expectType) == "string" then
		return function(argIndex, arg, nilOk)
			return checkType(funcName, argIndex, arg, expectType, nilOk)
		end
	else
		return function(argIndex, arg, expectType, nilOk)
			if type(expectType) == "table" then
				return checkTypeMulti(funcName, argIndex, arg, expectType, nilOk)
			else
				return checkType(funcName, argIndex, arg, expectType, nilOk)
			end
		end
	end
end

local function iterString(str)
	local indices = {}
	local find = string.find
	local function iterator(str, pos)
		i = indices[pos] or 0
		i = i + 1
		pos = pos + 1
		local _, endpos, char = find(str, "([%z\1-\127\194-\244][\128-\191]*)", pos)
		if char then
			indices[endpos] = i
			return endpos, char, i
		end
	end
	return iterator, str, 0
end

function export.chain(func1, func2, ...)
	return func1(func2(...))
end

--	map(function(number) return number ^ 2 end,
--		{ 1, 2, 3 })									--> { 1, 4, 9 }
--	map(function (char) return string.char(string.byte(char) - 0x20) end,
--		"abc")											--> { "A", "B", "C" }
function export.map(func, iterable)
	local check = _check 'map'
	check(1, func, "function")
	check(2, iterable, iterableTypes)
	
	local array = {}
	local iterator = type(iterable) == "string" and iterString or ipairs
	for i, val in iterator(iterable) do
		array[i] = func(val, i, iterable)
	end
	return array
end

function export.mapIter(func, iter, iterable, initVal)
	local check = _check 'mapIter'
	check(1, func, "function")
	check(2, iter, "function")
	check(3, iterable, iterableTypes, true)
	
	-- initVal could be anything
	
	local array = {}
	local i = 0
	for x, y in iter, iterable, initVal do
		i = i + 1
		array[i] = func(y, x, iterable)
	end
	return array
end

function export.mapIter2(func, iter, iterable, initVal)
	local check = _check 'mapIter2'
	check(1, func, "function")
	check(2, iter, "function")
	check(3, iterable, iterableTypes, true)
	
	-- initVal could be anything
	
	local array = {}
	local i = 0
	while true do
		local vals = { iter(iterable, initVal) }
		if not vals[1] then
			break
		end
		initVal = vals[1]
		i = i + 1
		array[i] = func(unpack(vals))
	end
	return array
end

function export.forEach(func, iterable)
	local check = _check 'forEach'
	check(1, func, "function")
	check(2, iterable, iterableTypes)
	
	local iterator = type(iterable) == "string" and iterString or ipairs
	for i, val in iterator(iterable) do
		func(val, i, iterable)
	end
	return nil
end

-------------------------------------------------
-- From [[http://lua-users.org/wiki/CurriedLua]].
-- reverse(...) : take some tuple and return a tuple of elements in reverse order
--
-- e.g. "reverse(1,2,3)" returns 3,2,1
local function reverse(...)
	-- reverse args by building a function to do it, similar to the unpack() example
	local function reverseHelper(acc, v, ...)
		if select('#', ...) == 0 then
			return v, acc()
		else
			return reverseHelper(function () return v, acc() end, ...)
		end
	end

	-- initial acc is the end of the list
	return reverseHelper(function () return end, ...)
end

function export.curry(func, numArgs)
	-- currying 2-argument functions seems to be the most popular application
	numArgs = numArgs or 2

	-- no sense currying for 1 arg or less
	if numArgs <= 1 then return func end

	-- helper takes an argTrace function, and number of arguments remaining to be applied
	local function curryHelper(argTrace, n)
		if n == 0 then
	 -- kick off argTrace, reverse argument list, and call the original function
			return func(reverse(argTrace()))
		else
			-- "push" argument (by building a wrapper function) and decrement n
			return function (onearg)
				return curryHelper(function () return onearg, argTrace() end, n - 1)
			end
		end
	end
	
	-- push the terminal case of argTrace into the function first
	return curryHelper(function () return end, numArgs)
end
-------------------------------------------------

local function capture(...)
	local vals = {...}
	return function()
		return unpack(vals)
	end
end

-- Find out what functions are returning by making a new version that still
-- returns the same values, but also logs them.
function export.logReturnValues(func, prefix)
	return function(...)
		local inputValues = capture(...)
		local returnValues = capture(func(...))
		if prefix then
			mw.log(prefix, inputValues())
			mw.log(returnValues())
		else
			mw.log(inputValues())
			mw.log(returnValues())
		end
		return returnValues()
	end
end

-- Make all functions in a table (for instance, a module's export table) log
-- their return values, but otherwise function normally.
function export.logAll(t)
	for k, v in pairs(t) do
		if type(v) == "function" then
			t[k] = export.logReturnValues(v, tostring(k))
		end
	end
	return t
end

-----M E M O I Z A T I O N-----
-- metamethod that does the work
-- Currently supports one argument and one return value.
local function callMethod(self, x)
	local output = self[x]
	if not output then
		output = self.func(x)
		self[x] = output
	end
	return output
end

-- shared metatable
local mt = { __call = callMethod }

-- Create callable table.
function export.memoize(func)
	return setmetatable({ func = func }, mt)
end
-------------------------------

--	some(function(val) return val % 2 == 0 end,
--		{ 2, 3, 5, 7, 11 })						--> true
function export.some(func, t)
	if t[i] then -- array
		for i, v in ipairs(t) do
			if func(v, i, t) then
				return true
			end
		end
	else
		for k, v in pairs(t) do
			if func(v, k, t) then
				return true
			end
		end
	end
	return false
end

--	all(function(val) return val % 2 == 0 end,
--		{ 2, 4, 8, 10, 12 })					--> true
function export.all(func, t)
	if t[i] then -- array
		for i, v in ipairs(t) do
			if not func(v, i, t) then
				return false
			end
		end
	else
		for k, v in pairs(t) do
			if not func(v, k, t) then
				return false
			end
		end
	end
	return true
end

local function toHex(number)
	return ("0x%X"):format(number)
end

local function printCodepoint(char)
	return toHex(ustring.codepoint(char))
end

function export.test(frame)
	local str = "ΒΓΔΖΘΚΛΜΝΞΠΡΣΣΤΦΧΨ"
	
	return table.concat(
		export.mapIter2(
			function(pos, char, i)
				return char .. " (" .. pos .. ", " .. i .. ") &rarr; " .. mw.ustring.char(mw.ustring.codepoint(char) + 0x20)
			end,
			iterString(str)),
		", "
	)
end

return export