Page 10 of 13

Re: Small Useful Functions

Posted: Tue Apr 14, 2015 1:18 am
by Inny

Code: Select all

function reduce(fn, array, initial)
  local result = initial or 0
  for i = 1, #array do
    result = fn(result, array[i], i)
  end
  return result
end
I'm fascinated with functional programming lately.

example:

Code: Select all

sum = function(array) return reduce(function(a, v) return a + v end, array) end
Actually, you're supposed to use bind with this

Code: Select all

function bind(fn, arg)
  return function(...) return fn(arg, ...) end
end

add = function(a, v) return a + v end
sum = bind(reduce, add)
Edit: szensk is right, the 'initial' parameter shouldn't be named zero, as it's meant to be the identity element for the group-operation it works over, and a multiplication based reduction would need to start with 1, not 0.

Re: Small Useful Functions

Posted: Tue Apr 14, 2015 5:42 am
by szensk
Very cool.

nit: Reduces parameter 'zero' should, for clarity, be called something like 'initial'. It is necessary for 'zero' to have a value other than 0 (for example: product) which must be why you have it as a parameter. That's a bit of unexpected value for 'zero', yea?

Re: Small Useful Functions

Posted: Wed May 13, 2015 3:47 pm
by Ref
Robin wrote:That last one can be one line:

Code: Select all

function inherit(parent, child)
	return setmetatable(child or {}, {__index = parent})
end
Ran into a hard to find problem related to the use of the above function.
Turns out that if the parent table has a table-field, it is passed to the child by reference.
If child's table-field changed, so will parent's and all other related children's.

Example: parent = {{x=100,y=100,w=100,h=100},color={0,0,255}}
Changing child's 'x' value will change the parent's 'x'.

Have created the following work-around.
Seems to work but would appreciate a more experienced programmer's check - improvement.
(Can only handle single level of table nesting.)

Code: Select all

function inherit( parent, child )					-- adds missing fields to child from parent
	for I, v in pairs( parent ) do
		if type( v ) == 'table' then				-- check for table in parent
			if not child[i] then child[i] = {} end	-- supply child table-field if not present
			setmetatable( child[i] , { __index = parent[i] } )
			end
		end
	setmetatable( child, { __index = parent } )          -- set for remaining fields
	end

Re: Small Useful Functions

Posted: Wed May 13, 2015 3:58 pm
by ivan
Here are a couple of nifty table.util functions.

Reverse (will only work with 'lists')

Code: Select all

--- Reverses the elements in a list
-- @param t Table
-- @param r Destination table (optional)
-- @return Reversed table
function table.reverse(t, r)
  r = r or t
  local n = #t
  if t == r then
    -- reverse in place
    for i = 1, floor(n/2) do
      local i2 = n - i + 1
      r[i], r[i2] = r[i2], r[i]
    end
  else
    -- reverse copy
    for i = 1, n do
      r[n - i + 1] = t[i]
    end
  end
  return r
end
Check if the table is empty (should work on any table - doesn't have to be a list)

Code: Select all

--- Checks if a table is empty
-- @param t Table
-- @return True if empty or false otherwise
function table.empty(t)
  return next(t) == nil
end

Re: Small Useful Functions

Posted: Wed May 13, 2015 9:14 pm
by Robin
Ref wrote:Ran into a hard to find problem related to the use of the above function.
Turns out that if the parent table has a table-field, it is passed to the child by reference.
If that's what you want, I would just forgo inheritance all-together, and just deep-copy the prototype:

Code: Select all

function deepcopy(t, cache)
    if type(t) ~= 'table' then
        return t
    end

    cache = cache or {}
    if cache[t] then
        return cache[t]
    end

    local new = {}

    cache[t] = new

    for key, value in pairs(t) do
        new[deepcopy(key, cache)] = deepcopy(value, cache)
    end

    return new
end

Re: Small Useful Functions

Posted: Wed May 13, 2015 10:33 pm
by s-ol
Floor all arguments:

Code: Select all

local __floor = math.floor
function math.floor(...)
  tbl = {...}
  for i,v in ipairs(tbl) do
    tbl[i] = __floor(v)
  end
  return unpack(tbl)
end
Perfect for use with love.transform:

Code: Select all

love.transform(math.floor(x, y))
This way it also still works for single arguments, so we can safely overwite math.floor itself.

Re: Small Useful Functions

Posted: Thu May 14, 2015 1:36 am
by Ref
Robin wrote:
Ref wrote:Ran into a hard to find problem related to the use of the above function.
Turns out that if the parent table has a table-field, it is passed to the child by reference.
If that's what you want, I would just forgo inheritance all-together, and just deep-copy the prototype:

Code: Select all

function deepcopy(t, cache)
    if type(t) ~= 'table' then
        return t
    end

    cache = cache or {}
    if cache[t] then
        return cache[t]
    end

    local new = {}

    cache[t] = new

    for key, value in pairs(t) do
        new[deepcopy(key, cache)] = deepcopy(value, cache)
    end

    return new
end
Yes and No.
Would like deep copy while retaining all fields in the child.
Any common fields between parent and child should default to child's.
Parent should be unaffected by process.

Re: Small Useful Functions

Posted: Thu May 14, 2015 7:22 am
by ivan
S0lll0s wrote:Floor all arguments:

Code: Select all

local __floor = math.floor
function math.floor(...)
  tbl = {...}
  for i,v in ipairs(tbl) do
    tbl[i] = __floor(v)
  end
  return unpack(tbl)
end
Here are two alternative implementations that don't create the intermediate table "tbl":

Code: Select all

local _unpack = table.unpack
local _floor = math.floor
function floor2(...)
  local tbl = {...}
  for i = 1, #tbl do
    tbl[i] = _floor(tbl[i])
  end
  return _unpack(tbl)
end

local _select = select
local _cache = {}
function floor3(...)
  local n = _select('#', ...)
  for i = 1, n do
    _cache[i] = _floor(_select(i, ...))
  end
  for i = n + 1, #_cache do
    _cache[i] = nil
  end
  return _unpack(_cache)
end

local _pack2
_pack2 = function(i, a1, a2, ...)
  _cache[i] = _floor(a1)
  if a2 then
    _pack2(i + 1, a2, ...)
  end
end
function floor4(...)
  local n = _select('#', ...)
  _pack2(1, ...)
  for i = n + 1, #_cache do
    _cache[i] = nil
  end
  return _unpack(_cache)
end

function floor5(a1, a2, ...)
  if a2 then return _floor(a1), floor5(a2, ...)
  else return _floor(a1) end
end

local tests = 100000
local input = {}
for i = 1, tests do
  input[i] = {}
  for j = 1, math.random(1, 10) do
    input[i][j] = math.random()*100
  end
end
local t1, m1 = os.clock(), collectgarbage("count")
for i = 1, tests do
  floor2(_unpack(input[i]))
end
local t2, m2 = os.clock(), collectgarbage("count")
for i = 1, tests do
  floor3(_unpack(input[i]))
end
local t3, m3 = os.clock(), collectgarbage("count")
for i = 1, tests do
  floor4(_unpack(input[i]))
end
local t4, m4 = os.clock(), collectgarbage("count")
for i = 1, tests do
  floor5(_unpack(input[i]))
end
local t5, m5 = os.clock(), collectgarbage("count")
print(t2 - t1, m2 - m1)
print(t3 - t2, m3 - m2)
print(t4 - t3, m4 - m3)
print(t5 - t4, m5 - m4)
Running on http://www.lua.org/cgi-bin/demo for 1-10 arguments:

Code: Select all

time:0.08	mem:14063.2890625 -- with the intermediate 'tbl', notice the memory usage
time:0.09	mem:1.453125 -- with select
time:0.08	mem:3.109375 -- with custom pack
time:0.08	mem:0 -- recursive, fastest for up to 10 arguments
and with 100 arguments:

Code: Select all

time:0.08	  mem:16175.0625  -- with the intermediate 'tbl'
time:0.19	mem:2.0  -- with select, notice the slowdown from the many calls to "select"
time:0.13	mem:138.78125  -- with custom pack, probably the most 'balanced' result
time:0.15	mem:0 -- recursive
In conclusion, I would advise against functions with variable number of arguments in critical code,
including "math.max", "math.min" and "unpack"

Re: Small Useful Functions

Posted: Mon Jun 08, 2015 2:41 am
by HugoBDesigner
A friend of mine asked me if stencils would be translated by "love.graphics.translate". They do.

However, he also stated that, if that's the case, scissors actually DON'T get translated. He asked me to fix that, and so I did:

Code: Select all

_translate = love.graphics.translate
_translateX, _translateY = {0}, {0}
_pop = love.graphics.pop
_origin = love.graphics.origin
_scissor = love.graphics.setScissor

function love.graphics.pop()
	table.remove(_translateX)
	table.remove(_translateY)
	_pop()
end

function love.graphics.translate(x, y)
	table.insert(_translateX, x)
	table.insert(_translateY, y)
	_translate(x, y)
end

function love.graphics.getTranslation()
	return love.graphics.getTranslationX(), love.graphics.getTranslationY()
end

function love.graphics.getTranslationX()
	local x = 0
	for i, v in ipairs(_translateX) do
		x = x + v
	end
	return x
end

function love.graphics.getTranslationY()
	local y = 0
	for i, v in ipairs(_translateY) do
		y = y + v
	end
	return y
end

function love.graphics.origin()
	_translateX = {0}
	_translateY = {0}
	_origin()
end

function love.graphics.setScissor(x, y, w, h)
	if x and type(x) == "number" then
		x = x + love.graphics.getTranslationX()
	end
	if y and type(y) == "number" then
		y = y + love.graphics.getTranslationY()
	end
	_scissor(x, y, w, h)
end
With this code, scissors will also be translated, and I also included some useful functions, like "love.graphics.getTranslation", which returns the result of all translation operations you've done. Really useful if you're using translations a lot and wants to draw something not being translated without doing all the "pops" and "translates" again ;)

Re: Small Useful Functions

Posted: Mon Jun 08, 2015 12:41 pm
by Lapin