Small Useful Functions

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
User avatar
Ref
Party member
Posts: 682
Joined: Wed May 02, 2012 11:05 pm

Re: Small extra functions

Post by Ref » Tue Apr 22, 2014 2:29 pm

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

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

Re: Small extra functions

Post by Ranguna259 » Tue Apr 22, 2014 10:03 pm

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

Check out my twitter.

User avatar
Roland_Yonaba
Inner party member
Posts: 1562
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Small extra functions

Post by Roland_Yonaba » Wed Apr 23, 2014 8:16 am

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

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

Re: Small extra functions

Post by Ref » Wed Apr 23, 2014 9:56 pm

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'.

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

Re: Small extra functions

Post by HugoBDesigner » Thu Apr 24, 2014 1:43 am

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

User avatar
Roland_Yonaba
Inner party member
Posts: 1562
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Small extra functions

Post by Roland_Yonaba » Thu Apr 24, 2014 7:19 pm

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

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

Re: Small extra functions

Post by Ref » Fri Apr 25, 2014 2:49 am

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.

User avatar
Roland_Yonaba
Inner party member
Posts: 1562
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Small extra functions

Post by Roland_Yonaba » Fri Apr 25, 2014 9:01 am

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

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

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

Re: Small extra functions

Post by HugoBDesigner » Tue May 06, 2014 5:36 am

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

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

Re: Small extra functions

Post by substitute541 » Tue May 06, 2014 9:43 am

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.

Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests