Module:Memoizer

From Fire Emblem Heroes Wiki
Jump to: navigation, search
Template-info.svg Documentation

Utility module which allows most functions to be memoized based on their arguments; the memoized function caches results for each unique set of arguments, avoiding expensive recomputations if the function is known to be pure, such as Cargo queries. The module exposes a single function, memoizer, which accepts two arguments:

  • func: The function to be memoized. It may accept any number of arguments, and it may return nil. It must not return more than one value (extra values are discarded) or call itself (recursive functions cannot access memoized versions of themselves).
  • keyfn: If provided, the memoizer calls this object using the passed arguments to construct a key for the arguments; otherwise, the first argument is used as the key. Invocations that produce the same key will return the same cached value. If the returned value is a table or userdata, it is referenced by all invocations of the same key and never duplicated.

Example[edit source]

local memoize = require 'Module:Memoizer'.memoizer
local origin = memoize(function (hero)
  mw.log(hero)
  local result = mw.ext.cargo.query('Units', 'Origin', {where = ("_pageName='%s'"):format(hero)})[1]
  return result and result.Origin or 'Unknown'
end)

mw.log(origin('Wrys: Kindly Priest'))
-- Wrys: Kindly Priest
-- Fire Emblem: Shadow Dragon and the Blade of Light

mw.log(origin('Wrys: Kindly Priest'))
-- Fire Emblem: Shadow Dragon and the Blade of Light

mw.log(origin('Clarine: Refined Noble'))
-- Clarine: Refined Noble
-- Fire Emblem: The Binding Blade

mw.log(origin('Wrys: Kindly Priest'))
-- Fire Emblem: Shadow Dragon and the Blade of Light

mw.log(origin('Clarine: Refined Noble'))
-- Fire Emblem: The Binding Blade

mw.log(origin('INVALID'))
-- INVALID
-- Unknown

mw.log(origin('INVALID'))
-- Unknown



local sum = memoize(function (x, y)
  mw.log(x .. ' + ' .. y .. ' =')
  return x + y
end, function (x, y)
  return math.min(x, y) .. ';' .. math.max(x, y)
end)

mw.log(sum(2, 4))
-- 2 + 4 =
-- 6

mw.log(sum(2, 3))
-- 2 + 3 =
-- 5

mw.log(sum(2, 4))
-- 6

mw.log(sum(4, 2))
-- 6

mw.log(sum(3, 3))
-- 3 + 3 =
-- 6
local nil_tag = {}

local memoizer = function (func, keyfn)
	local cache = setmetatable({}, {__mode = 'k'})
	if not keyfn then
		return function (...)
			local key = ...
			key = key == nil and nil_tag or key
			local v = rawget(cache, key)
			if rawequal(v, nil_tag) then
				return nil
			elseif not rawequal(v, nil) then
				return v
			end
			
			v = func(...)
			rawset(cache, key, rawequal(v, nil) and nil_tag or v)
			return v
		end
	end

	return function (...)
		local key = keyfn(...)
		key = key == nil and nil_tag or key
		local v = rawget(cache, key)
		if rawequal(v, nil_tag) then
			return nil
		elseif not rawequal(v, nil) then
			return v
		end
		
		v = func(...)
		rawset(cache, key, rawequal(v, nil) and nil_tag or v)
		return v
	end
end

return {
	memoizer = memoizer,
}