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:
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