## Small Useful Functions

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Ref
Party member
Posts: 682
Joined: Wed May 02, 2012 11:05 pm

### Re: Small extra functions

Substitute541, you might want to consider add table.sort(a) to median function - just nit picking.

Ranguna259
Party member
Posts: 911
Joined: Tue Jun 18, 2013 10:58 pm
Location: I'm right next to you

### Re: Small extra functions

Here are two functions do use in a for loop:

Code: Select all

chars = function(text)
local i=0
local n = #text
--print(text)
return function()
i=i+1
if i <= n then return string.sub(text,i,i),i end
end
end
words = function(text,spaces)
local i=0
local wordss = {}
local onspace = false
local word = {}
for l,p in chars(text) do
if l == ' ' then
if not onspace then
if #word > 0 then
wordss[#wordss+1] = table.concat(word)
end
if spaces then
word = {' '}
else
word = {}
end
onspace = true
else
if spaces then
word[#word+1] = ' '
end
end
else
if onspace then
if #word > 0 then
wordss[#wordss+1] = table.concat(word)
end
word = {l}
onspace = false
else
word[#word+1] = l
end
end
end
if #word > 0 then
wordss[#wordss+1] = table.concat(word)
end
return function()
i = i + 1
if i <= #wordss then return wordss[i] end
end
end
The chars functions iterates over all character in a text and returns the character and it's position on the text, use:

Code: Select all

for l,p in chars('hello') do
print('Character, "'..l..'" at position '..p)
end
Will print:

Code: Select all

Character, "h" at position 1
Character, "e" at position 2
Character, "l" at position 3
Character, "l" at position 4
Character, "o" at position 5
The words function iterates over all words on the text, this function also has the option to ignore spaces, use:

Code: Select all

for w,p in words('hello there you') do
print('word, "'..l..'"')
end
Will print:

Code: Select all

word, "hello"
word, "there"
word, "you"
Use:

Code: Select all

for w in words('hello there you',true) do
print('word, "'..l..'"')
end
Will print:

Code: Select all

word, "hello"
word, " "
word, "there"
word, " "
word, "you"
LoveDebug- A library that will help you debug your game with an on-screen fully interactive lua console, you can even do code hotswapping

Roland_Yonaba
Inner party member
Posts: 1562
Joined: Tue Jun 21, 2011 6:08 pm
Contact:

### Re: Small extra functions

Interesting. I'd like to provide some alternate implementations, using string pattern matching.
In case I want to iterate on each single character of a given string, i can use string.gmatch.

Code: Select all

local str = 'hello'
for char in str:gmatch('.') do
print('character '..char)
end

This one is highly flexible just because if I want to count anything else, I can just change the pattern matching passed to the iterator.
For instance, we can easily iterate on each uppercase character in the string 'HeLlO'

Code: Select all

local str = 'HeLlO'
for uppercase_char in str:gmatch('%u') do
print('character '..uppercase_char)
end

However, this doesn't produce the count of characters. In case I want this though, I can provide an iterator function which wraps string.gmatch and provides an incremental count along with the returned value of string.gmatch as long as this capture is not nil.

Code: Select all

local function gmatch(s,pat)
local i = 0
local match = string.gmatch(s,pat)
return function()
i = i+1
local capture = match()
if capture then return i, capture end
end
end

-- example, provide de the count of characters in a string
local str = 'hello'
for count, char in gmatch(str,'.') do
print('character '..char, 'count '..count)
end

-- Or count the lowercase characters
local str = 'HeLlO'
for count, char in gmatch(str,'%l') do
print('character '..char, 'count '..count)
end


Second, for words, it is quite the same. If I consider that a word is a sequence of alphanumeric characters, i'll use '%w+' as a pattern for capturing words.

Code: Select all

str = 'hello the world'
for w in str:gmatch('%w+') do
print('word '..w)
end

However, if things can be trickier than that, I can roughly assume that a words is a sequence of characters delimited by whitespaces.
And in that case, I can use the pattern '%s*([^%s]+)%s*' to capture a word.

Code: Select all

str = 'hello the world'
for w in str:gmatch('%s*([^%s]+)%s*') do
print('word '..w)
end


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

### Re: Small extra functions

Can't beat the great Roland!
Still trying to avoid the string library but if I have to split up a string into words, I resort to the old split function

Code: Select all

function string:split( sep )
local sep, fields = sep or ",", {}
local pattern = string.format( "([^%s]+)", sep )
self:gsub( pattern, function(c) fields[ #fields + 1 ] = c end )
return fields
end

where sep is set to " " and out pops a table of words.
(Of course, the split function is also great for comma separated text ...)
EDIT: Both this approach and Roland's has the problem that if there is a punctuation mark at the end of the string, it will be included in the last 'word'.

HugoBDesigner
Party member
Posts: 403
Joined: Mon Feb 24, 2014 6:54 pm
Location: Somewhere in Brazil
Contact:

### Re: Small extra functions

Couldn't you just add this before the return?

Code: Select all

for i, v in pairs(field) do
if string.sub(v, -1, -1) == "." then
v = string.sub(v, 1, -2)
end
end

Or something more improved, like having multiple punctuation marks or checking them in the whole word. I couldn't come up with anything smaller than a for-do loop for each word...
HugoBDesigner - my Blog

Roland_Yonaba
Inner party member
Posts: 1562
Joined: Tue Jun 21, 2011 6:08 pm
Contact:

### Re: Small extra functions

Ref wrote:EDIT: Both this approach and Roland's has the problem that if there is a punctuation mark at the end of the string, it will be included in the last 'word'.

Code: Select all

local str = 'hello the world.'
for w in str:gmatch('(%w+)%p*') do
print('word '..w)
end

This seems to solve them problem, I guess.

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

### Re: Small extra functions

I guess you can always come up with a problem if you really want.
Roland, your last version can't handle "The sample was 12.56 cm long!".
It breaks up 12.56 into 12 and 56.

Roland_Yonaba
Inner party member
Posts: 1562
Joined: Tue Jun 21, 2011 6:08 pm
Contact:

### Re: Small extra functions

Ref wrote:I guess you can always come up with a problem if you really want.
Ah, Great Ref is soo right...
Ref wrote: Roland, your last version can't handle "The sample was 12.56 cm long!".
It breaks up 12.56 into 12 and 56.
Well, in that case, I can just wrap string.gmatch again in an interator function that will trim any ending punctuation character on each capture before returning it.
Could'nt think of anything better.

Code: Select all

local function gmatch(s,pat)
local match = string.gmatch(s,pat)
return function()
local capture = match()
if capture then return (capture:gsub(('%p\$'),'')) end
end
end

local str = 'The sample was 12.56 cm long!'
for w in gmatch(str,'([^%s]+)') do
print(w)
end


HugoBDesigner
Party member
Posts: 403
Joined: Mon Feb 24, 2014 6:54 pm
Location: Somewhere in Brazil
Contact:

### Re: Small extra functions

For my newest code doodle, I [kind of] re-made LÖVE's circle system. Now, instead of operating math.floor for the amount of sides, it just makes one of the sides smaller. Here's the code for it (the "return" part can be removed, I did it just to get the inner lines):

Code: Select all

function love.graphics.shape(mode, x, y, rd, sides)
local points = {}
for i = 1, math.ceil(sides) do
local px = x
local py = y
local a = math.pi*2/sides*i
px = px + math.cos(a)*rd
py = py + math.sin(a)*rd
table.insert(points, px)
table.insert(points, py)
end
love.graphics.polygon(mode, points)
return points
end

HugoBDesigner - my Blog

substitute541
Party member
Posts: 484
Joined: Fri Aug 24, 2012 9:04 am
Location: Southern Leyte, Visayas, Philippines
Contact:

### Re: Small extra functions

Here are two functions that approximate sine and cosine (haven't benchmarked it yet though):

Code: Select all

local TPI = math.pi*2
local PI = math.pi
local math_abs = math.abs
local math_floor = math.floor

function fastSin(x)
local tpi = TPI
local pi  = PI
local t = math_abs(-2*math_floor(x/tpi+0.75) + x/pi + 0.5)
return 2*t*t*(3-t*2)-1
end

function fastCos(x)
local tpi = TPI
local pi  = PI
local t = math_abs(-2*math_floor(x/tpi+1) + x/pi + 1)
return 2*t*t*(3-t*2)-1
end

Based on an article in nVidia GPU Gems about fast approximate sine and cosine implementation by interpolating triangle waves.

Error is at most +- 1% of the amplitude.

Edit: Tested it with 10,000 values with fastCos and math.cos and it usually performs better than math.cos. On lower iterations (about a few thousand) math.cos still wins. So you should really only use this on a huge loop with tons of cosine and sine calculations and if you don't mind the error.
Currently designing themes for WordPress.

Sometimes lurks around the forum.

### Who is online

Users browsing this forum: No registered users and 4 guests