## Small Useful Functions

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

### Re: Small Useful Functions

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

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.
Last edited by Inny on Wed Apr 15, 2015 12:32 am, edited 1 time in total.

szensk
Party member
Posts: 155
Joined: Sat Jan 19, 2013 3:57 am

### Re: Small Useful Functions

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?

Ref
Party member
Posts: 690
Joined: Wed May 02, 2012 11:05 pm

### Re: Small Useful Functions

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


ivan
Party member
Posts: 1613
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

### Re: Small Useful Functions

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

Robin
The Omniscient
Posts: 6506
Joined: Fri Feb 20, 2009 4:29 pm
Location: The Netherlands
Contact:

### Re: Small Useful Functions

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

s-ol
Party member
Posts: 1077
Joined: Mon Sep 15, 2014 7:41 pm
Location: Cologne, Germany
Contact:

### Re: Small Useful Functions

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.

s-ol.nu /blog  -  p.s-ol.be /st8.lua  -  g.s-ol.be /gtglg /curcur

Code: Select all

print( type(love) )
if false then
baby:hurt(me)
end

Ref
Party member
Posts: 690
Joined: Wed May 02, 2012 11:05 pm

### Re: Small Useful Functions

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.

ivan
Party member
Posts: 1613
Joined: Fri Mar 07, 2008 1:39 pm
Contact:

### Re: Small Useful Functions

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"

HugoBDesigner
Party member
Posts: 403
Joined: Mon Feb 24, 2014 6:54 pm
Location: Above the Pocket Dimension
Contact:

### Re: Small Useful Functions

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
HugoBDesigner - my Blog

Lapin
Prole
Posts: 17
Joined: Fri Mar 20, 2015 11:53 am

### Who is online

Users browsing this forum: vinperdom and 28 guests