--[[

	memoize creates a function as a wrapper that caches the last outputs of a function.

	This is useful if you know that the function should return the same output every

	time it is run with the same inputs. The function should only return an output, and

	not have any side effects. These side effects are not cached.



	Without memoize's caching, even though the function ouputs the same values, the

	memory locations of the values are different; tables made in the function, even if

	they have the same values, won't be the same tables.



	memoize only caches the last set of inputs and ouputs. This means that it is only

	helpful when the function is likely to be called with the same inputs multiple

	times in a row. This is the case with most Roact use cases.



	Note that memoize only does a   ** shallow check on table inputs **   . This means

	that if the same table is input but the elements of the table are different then

	it will be assumed that the table has not changed.



	In addition to all the previous warnings, memoize strips trailing nils. This means

	that if foo is a memoized function and we call foo(), then foo(nil) will return a

	cached value. This is opposed to how print handles input. print() only outputs a

	new line, but print(nil) outputs "nil". This is because varargs can detect the

	number of arguments passed in. So, be careful when using memoize with varargs.

	Trailing nils will be stripped.



	The wrapper can take any number of inputs and give any number of outputs.

	Leading and interspersed nils are handled gracefully. Trailing nils on the input

	are stripped.

]]

local function captureSize(...)

	return {...}, select("#", ...)

end



local function memoize(func)

	assert(type(func) == "function", "memoize requires a function to memoize")



	local lastArgs

	local lastNumArgs

	local lastOutput

	local lastNumOutput



	return function(...)

		local numArgs = select("#", ...)



		while numArgs > 0 and select(numArgs, ...) == nil do

			numArgs = numArgs - 1

		end



		if numArgs ~= lastNumArgs then

			lastArgs = {...}

			lastNumArgs = numArgs

			lastOutput, lastNumOutput = captureSize(func(...))

			return unpack(lastOutput, 1, lastNumOutput)

		end



		for i = 1, lastNumArgs do

			if select(i, ...) ~= lastArgs[i] then

				lastArgs = {...}

				lastOutput, lastNumOutput = captureSize(func(...))

				break

			end

		end



		return unpack(lastOutput, 1, lastNumOutput)

	end

end



return memoize