Page 3 of 13

Re: Small extra functions

Posted: Tue Apr 22, 2014 2:29 pm
by Ref
Substitute541, you might want to consider add table.sort(a) to median function - just nit picking.

Re: Small extra functions

Posted: Tue Apr 22, 2014 10:03 pm
by Ranguna259
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"

Re: Small extra functions

Posted: Wed Apr 23, 2014 8:16 am
by Roland_Yonaba
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

Re: Small extra functions

Posted: Wed Apr 23, 2014 9:56 pm
by Ref
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'.

Re: Small extra functions

Posted: Thu Apr 24, 2014 1:43 am
by HugoBDesigner
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...

Re: Small extra functions

Posted: Thu Apr 24, 2014 7:19 pm
by Roland_Yonaba
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. :)

Re: Small extra functions

Posted: Fri Apr 25, 2014 2:49 am
by Ref
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.

Re: Small extra functions

Posted: Fri Apr 25, 2014 9:01 am
by Roland_Yonaba
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

Re: Small extra functions

Posted: Tue May 06, 2014 5:36 am
by HugoBDesigner
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

Re: Small extra functions

Posted: Tue May 06, 2014 9:43 am
by substitute541
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.