Syntax Shortening / Useful Functions

Sometimes when writing functions that are short it seemed kind of verbose to add the function() end syntax, and also the little yellow function markers on the sides can get kind of annoying when there are a lot of anonymous functions, so I created this script that will produce a function without the need for as much heavy formatting.

Syntax:

If called without args it works like a standard function.

local Test = Fn( [[ print("Hello World") ]] Test() - prints Hello World

If called with args then the input is a string or literal string which ignores syntax and searches for any letter characters separated by spaces or any non letter character to be passed to the function as arguments.

local Test = Fn("a,b",[[ print(b,a) ]] Test(World,Hello) - prints Hello World

local Test = Fn("a b",[[ print(b,a) ]] Test(World,Hello) - prints Hello World

local Test = Fn("wrdA wrdB",[[ print(wrdB,wrdA) ]] Test(World,Hello) - prints Hello World

Finally, if args string contains ... then the function becomes a vararg function and adds all values passed to it to a table with the name specified after the vararg expression in the args string.

local Test = Fn("... txt", [[ print(txt[1],txt[2]) ]] Test(Hello,World) - prints Hello World

Below is the code for the function


function Fn(args,func)
    
    local Modif,Execute,Arguments = "" if func == nil then do Execute = args Arguments = "" end
    elseif func then do Execute = func if string.match(args,"%.%.%.") then Arguments = "..." 
    local Name = string.match(args,"%a+") Modif = [[ local ]]..Name..[[ = {...} ]]
    else Arguments = "" for Arg in string.gmatch(args,"%a+") do if Arguments ~= "" then 
    Arguments = Arguments .."," end Arguments = Arguments..Arg end end end end
    local src = [[ return function( ]]..Arguments..[[ ) ]]..Modif..Execute..[[ end ]] 
    return loadstring(src)()
end

Function - round(number,dps) - Rounds a number to a given number of decimal places

Description:

These function round a number up or down to the specified number of decimal places by factor of 5. (If the number after the decimal place specified is >= 5 then the result is rounded up. If it is < 5, then it is rounded down.)

local Number = 356.4645

You can specify either a positive, negative, or zero for the number of decimal places for the round function .

round(Number,2) - Returns 356.46 (Rounded down)

round(Number,3) - Returns 356.465 (Rounded up)

round(Number,0) - Returns 356

round(Number,-1) - Returns 360

Below I have provided multiple versions of the round function based off of suggestions I have recieved, because my original function was a little bit more robust than needed. All of the functions will return the same results, so usage is entirely preferential. Also you can see that there are multiple ways to do things and different angles to approach them from.

The first is a function I created based off of a suggestion by @dave1707 to use string.format to get the rounded results. (You have to remove the space between the less than or equal to sign for it to work … Annoying html encoding stuff)


function round(num,dps)
    if num > 0 then return string.format("%"..tostring(dps/10).."f",num)
    elseif num < = 0 then return string.format("%"..tostring(dps * -1).."d",num) 
    end
end

This is one which was made by @Ignatz which uses math.floor to evaluate its results.


function round(num,dps)
    local f = 10 ^ dps
    return math.floor(num * f + 0.5 ) / f  
end

Finally, my original round function which uses a combination of math.floor and math.ceil, but is a little bit more robust than the other two.


-- Rounds Number To Given Decimal Places (Up/Down)
function round(num,dps)
    local mult = 10^(dps or 0)
    if (num * mult) % 1 >= 0.5 then return math.ceil(num * mult)/mult 
    elseif (num * mult) % 1 < 0.5 then return math.floor(num * mult)/mult end
end

@Beckett2000 Nice job on the round function. Writing functions like that gives you an idea of what’s involved in doing what seems natural when rounding numbers on a piece of paper. But the built in string.format function already does that. See the example below.


function setup()
    a=1.666666
    print("starting number  ",a)

    print()    
    print("using string.format")
    print(string.format("%.1f",a))
    print(string.format("%.2f",a))
    print(string.format("%.3f",a))
    print(string.format("%.4f",a))
    print(string.format("%.5f",a))
    
    print()
    print("using round function")
    print(round(a,1))
    print(round(a,2))
    print(round(a,3))
    print(round(a,4))
    print(round(a,5))
end

-- Rounds Number To Given Decimal Places (Up/Down)
function round(num,dps)
    local mult = 10^(dps or 0)
    if (num * mult) % 1 >= 0.5 then return math.ceil(num * mult)/mult 
    elseif (num * mult) % 1 < 0.5 then return math.floor(num * mult)/mult end
end

Function - CompFPS(samples,dps) - Adjustable FPS display. Specify samples and decimal places and returns string “(Current FPS) FPS”

@dave1707 - Thanks, I didn’t know that I could use the string.format function to do that. I just started working with the strings library and pattern matching this week and have been trying to experiment with it to make different functions to perform actions in the GUI system that im building. I was mainly using the function to round built into the function below. It computes a running average of 1/DeltaTime values and returns the approximate FPS in a string which can be printed on the screen. I changed out the round function for string.format. I generally use it with 1dp and 60 samples and it runs pretty smoothly.

Description:

This function will return a string with the running average of 1/Deltatime - (Frames rate per second of current frame) after the number of samples have been collected. To use you just have to put the less than or equal sign together towards the end. I had to separate them because the post would not post correctly with it left together.
If for example you assign the return value to text on the screen, it will dynamically update and give you a view of your current FPS.

Note: To use, the function must be called by a draw() function.

Note: The higher the sample count the slower(but more smoothly) the FPS display will update.


-- Gets FPS Running Average for Sample Distribution 
-- Rounds to (dps) Decimal Places
function CompFPS(samples,dps)
    
    if FPS == nil then FPS = {} end if FPS.times == nil then FPS.times = {} end 
    if FPS.output == nil then FPS.output = 60 end
    
    if FPS.tick == nil then FPS.tick = 1
    elseif FPS.tick and FPS.tick < samples then FPS.tick = FPS.tick + 1 
    elseif FPS.tick and FPS.tick == samples then FPS.tick = 1 end
    local Tick,Times = FPS.tick,FPS.times
    
    -- Stores FPS DeltaTime Values
    if #Times < samples then table.insert(Times,Tick,1/DeltaTime) 
    elseif #Times == samples then Times[Tick] = 1/DeltaTime end

    -- If Sample Number Has Been Collected
    if Tick == samples then local Sum,fps = 0,0 
     for _,Time in ipairs(Times) do Sum = Sum + Time end 
     fps = ((Sum/#Times)/10) + (FPS.output * 0.9)
     local Decimals if dps > 0 then Decimals = "%"..tostring(dps/10).."f" 
     elseif dps == 0 then Decimals = "%01d" end
     fps = tonumber(string.format(Decimals,fps))
    
     if fps < = 60 then FPS.output = fps else FPS.output = 60 end end
    return tostring(FPS.output).." FPS" 
       
end

@Beckett2000 The CompFPS runs nice. I like the way you can change the sample rate and the decimal places. One question, do you normally format your programs that way. It makes for a smaller listing, but I had to reformat it to make it easier for me to look it over. I’m not saying there’s anything wrong with it, because everyone has their own style. But I was just wondering if that’s your style of coding.

@dave1707 Thanks. Generally I do write my programs like that so that they take up as little space as possible in the code interface while still being readable. I used to when I was first working with Lua separate them by line, but I started putting them together like that so that I could more easily navigate them. For a smaller function like that it isn’t particularly necessary and I just do it out of habit, but my menu function for example is currently about 300 lines in that format, so if it was expanded it would be about 700 or more and much more cumbersome to traverse. Also, normally I write the functions even more compressed than that, but I separated it slightly so it would make a little more sense to read on the forum.

Function - table.getS(table) - Get number of values in Array/Table

Description:

This is a small utility function to extend the table library. It will return the linear traversal of an array, but unlike the # operator or table.getn functions, it can also return the number of values in a table or the combination of linear traversal and defined values, which can be useful in evaluating if a table is actually an array or a table.

Here is an example table

Table = {50,60,70,nil,80,"string"}

Table[foo] = 40 Table[bar] = "string" Table[1.1] = "string"

The first example uses table.getS without options. The return would be the same as calling table.getS with the option ‘defined’. It returns the number of defined values in a given table.

table.getS(Table) - Returns 8

This is using the option ‘linear’ which works the same as table.getn or the # operator for a linear traversal of values starting at 1

table.getS(Table,"linear") - Returns 6

Finally, using the option ‘mixed’ returns the same as defined, but it will also perform a standard linear traversal and catch holes.

table.getS(Table,"mixed") - Returns 9

Using the ‘mixed’ option can be useful in evaluating if a table is a table or an array with something like the format below

if table.getS(Table,"mixed") == #Table then - Array

if table.getS(Table,"mixed") ~= #Table then - Table

Many times the # operator is used to evaluate if a table is a table or an array, but this would not work in a case like below

Table = {} Table[1] = 40 Table[2] = 60 Table[bar] = "string" Table[1.1] = "string"

Using the (#) operator here would return 2, and if you were using this to check, evaluate as an array, but this will not happen with the conditionals above.


-- Returns size of table or array -- Defaults to 'defined'
function table.getS(table,option)
    
    local Size -- Local Size Storage variable
    if type(table) ~= "table" then 
     error("Invalid argument no.1 to getS table expected, was "..type(table))
    
    elseif option == nil or option == "defined" then -- all defined entries
     Size = 0 for _,_ in pairs(table) do Size = Size + 1 end 
    elseif option == "linear" then Size = #table -- linear traversal
    elseif option == "mixed" then  -- includes traversal holes in count
     Size = #table for key,_ in pairs(table) do
      if type(key) == "number" then if key < 1 or key % 1 ~= 0 then Size = Size + 1 end  
      elseif type(key) ~= "number" then Size = Size + 1 end end
    
    elseif option and option ~= "linear" and option ~= "defined" then
     error("Invalid option to getS must be 'linear', 'defined' or 'mixed', was "
    ..tostring(option)) end
    
    return Size 
end 

This is an extension of my function shortener from before. Previously every time the function was called it would have to use loadstring, which can be rather inefficient, especially in loops. This function stores the function values in a weak table keyed to their string values and performs a search if the function is recalled. The table is depleted every garbage collection cycle to avoid memory cluttering. As an example, looping the former function 10000 times would produce significant lag as loadstring would be used 10000 times. With memoization loadstring would only be used about 5 times, varying depending on the aggressiveness of the garbage collection cycle.

See my former post for complete usage


-- Creates Function Short Caller Inputs - Strings/Literal Strings
 -- A vararg function is denoted by args = "... (Variable table name)"
function Fn(args,func)
    
    local Modif,Execute,Arguments = "" if func == nil then do Execute = args Arguments = "" end
    elseif func then do Execute = func if string.match(args,"%.%.%.") then Arguments = "..." 
    local Name = string.match(args,"%a+") Modif = [[ local ]]..Name..[[ = {...} ]]
    else Arguments = "" for Arg in string.gmatch(args,"%a+") do if Arguments ~= "" then 
    Arguments = Arguments .."," end Arguments = Arguments..Arg end end end end
    local src = [[ return function( ]]..Arguments..[[ ) ]]..Modif..Execute..[[ end ]] 

     local Memoize = { -- Memoize MetaTable Template
     __call  = function(self,string)
      if self.memoized == nil then self.memoized = {} end
       if self.memoized[string] == nil then 
       self.memoized[string] = loadstring(string)() end
      return self.memoized[string] end,
     __mode = "v" }  -- Weak table values
     
     if FnMem == nil then FnMem = {} end
     setmetatable(FnMem,Memoize) return FnMem(src)

end

Function - table.indexOf(array,...) - (R3) - Returns indexes of number/string/table/function in array.

@dave1707 I have been trying to work on the readability of my functions before posting them a little more so they are easier to go over.

Description:

This is another table extension which is inspired by the JavaScript indexOf function. It will traverse a table and return the index or indexes of occurance of a particular number, string, function, or table. Bear in mind however that a function or table in Lua is only equal to itself. This function does not compare tables or functions, but instead will return nil if the table or function is not a pointer to the same one. (I am working on a comparison function however for this purpose) The function supports a variable number of arguments and will return either the index of occurrence or nil if not found for all arguments.

Note: This function is only intended for arrays, not tables. If a table is attempted to be searched, it will in most cases throw an error, though some may not and may return unexpected results, so try not to use the function for tables. If you want to make the function catch all non arrays then you can use the conditionals from the table.getS() above, but I didn’t include them in this function so that it could be standalone for those who wanted to use it but not implement the other.

Note 2 : Unlike some other implementations of indexOf, this function can also jump over holes in an array, so having non sequential arrays is not an issue as long as they have numerical keys. Technically it could be easily changed to also return keys of a keyed table with non numerical keys, but I wanted to keep the function true to its original purpose, and it would have to be changed somewhat to support searching for sub tables.

Usage Examples:

local A = {} local B = function() end
Table = {12,20,"Foo","Bar",nil,"Foo",A,B}

The first example shows how the indexOf can be used to search for a single value in an array. When there is only two arguments, the function will return all instances of occurrence in sequential order.

table.indexOf(Table,"Foo") - Returns 3,6

You can also specify multiple elements in a single call and the function will return the first instance of occurrence for each argument

table.indexOf(Table,"Foo","Bar",12,20) - Returns 3,4,1,2

Entries that are passed to the function that are not found will return nil for each occurrence.

table.indexOf(Table,"Foo","FooBar","Bar","Bear") - Returns 3,nil,4,nil

table.indexOf(Table,"Hello","World") - Returns nil,nil

Entries passed to the function that are nil will also return nil. Generally you wouldn’t search for nil explicitly in an array (though that will technically work as well), but is more likely to happen when a variable is not defined and you attempt to search for it.

table.indexOf(Table,"Foo",nil) - Returns 3,nil

table.indexOf(Table,nil,"Bar",nil,"Foo",nil) - Returns nil,4,nil,3,nil

table.indexOf(Table,nil,nil) - Returns nil,nil

You will always get a return for every argument that you pass to the function. While it may seem repetitious to have an output like the last above, while it doesn’t matter for variable assignment, it can be useful if you are doing things like printing results on the screen or for debug purposes.

Finally, you can also search for tables and functions within an array, but in Lua, tables and functions are only equal to themselves or nil, so in order to search for one you need to search for a pointer to the table or function which would generally be some kind of variable. (or if the function is global then you can just search for the function name)

table.indexOf(Table,A,B) - Returns 7,8

table.indexOf(Table,{}) - Returns nil

Even though the last example above attempts to search for a table that is the same as the one which is in the array, the return is nil because even though it’s values are the same, it is not the same table.

Here is the actual function:


-- Returns Index of Arguments in Array
function table.indexOf(array,...)
    
    if type(array) ~= "table" or #array == 0 and array ~= {} then 
     error("Invalid argument no.1 to indexOf array expected, was "..type(array))
    else local Vals,Indexes,Pos = {...},{},1
       
     local function Check(...) -- Checks if Arguments are nil
      for i = Pos,arg.n do if Vals[i] == nil then Pos = Pos + 1
       elseif Vals[i] then break end end end Check(...) 
    
     for _,entry in pairs(Vals) do 
      for index,val in pairs(array) do  
        
       if entry == val and type(index) == "number" then 
        table.insert(Indexes,Pos,index) Pos = Pos + 1 Check(...) 
        if #Vals ~= 1 then break end
       elseif index == #array then 
        if #Vals ~= 1 then Pos = Pos + 1 Check(...)
        elseif #Vals == 1 and Pos == 1 then Pos = Pos + 1 
     end end end end
         
    return unpack(Indexes,1,Pos - 1)
    
end end

Change log:

Update R2 - September 23, 2013 - Revision no.1 had an undefined variable ‘Length’ that would always evaluate to nil and prevent the return of multiple indexes if there were only two arguments to the function. Currently the only bug I am aware of is if you search for a table in the array that is not found and then search for an explicit nil (An argument that is undefined) the order of returns will be different than the input order. (The nil will be put as the last return instead of its sequential order) I’m working at fixing this, but until I do, try not to do this until I can resolve the error or you will receive unexpected returns.

Update R3 - Fully Functional. I figured out that I just was not performing a check for nil arguments after a value was not found, so it wouldn’t return another value until the next non explicit nil element was searched. This didn’t only affect tables, I just happened to catch it with that. Now it will as I said before truly return a numerical index or nil for every argument passed to it.

@Beckett2000 Another interesting function. I’m not sure what I would use it for at this time. I don’t recall having to search a table in that manner, but I’m sure someone will have a use for it. If they don’t need the function, then they can learn from how you wrote the code. The code was easier to read, but I still had to reformat it to really understand what was happening. I like my “end” statements to line up with their keywords, and also indentation. But like I said earlier, don’t change your style to suit others. Keep posting examples, everyone benefits.

@dave1707 Thank you. Right now I’m building those utility functions mostly to try and build my own timer class for keeping track of things. Right now I created some timers that I posted in another topic, but the first revision would get really out of sync as the draw count changed and the second is stable, but relies heavily on using the tween library to store transition and timing data. I wanted it to function on its own, that and I wanted to actually get a sense of how the tween library is able to keep accurate time without using it as a dependency for the timers I’m using. The reason I tend to clump the ends together is mostly just to save space and keep my line count down when working with the code.

@Beckett2000 - it’s great to have utilities like this.

Your round function could probably be shortened to

function round(num,dps)
   local f=10^dps
   return math.floor(num*f+0.5)/f
end

@Ignatz Thank you, that is a much shorter way to write the function. I am going to keep updating this topic as I make more utility functions. I am also working on creating a timer library and a function that can scan through tables/multidimensional arrays.

@Beckett2000 - we are going to be able to share our code soon, and this kind of function will be useful. Can I suggest that as you complete them, you prefix the code with usage instructions, and where the purpose may not be obvious (eg function shortened) it may be a good idea to give an example of where it would be used.

But please hang on to these, we will have a display shelf to put them on soon!!

@Ignatz I did get a chance to look at the example for Codea sharing environment that you proposed using projects inside Codea, and I think its a really good idea, especially since Apple wont allow the former sharing methods that were in Codea. I look forward to using it! I’ll add the tags at the beginning as you suggested plus a usage section for some of the code that isn as clear if that would help too. I was also thinking to use this topic as a reference section as well, so if someone proposed a better/different way that worked to do something, I would add to / change the code in my original topic post as well and credit them with its creation. That way, the topic could be used by people to go and see/pick functions that they could use, and at the same time it would would contain many different ways of doing things so that people new to Lua/Codea could see how things are built and how they function. Let me know what you think!

Just a preference, but I would assume a function named round would be something like

function round(i)
    return math.floor(i + 0.5)
end

Not usually clipping the decimal point.

@SkyTheCoder The reason for the decimal point clipping is so you can do things like in the CompFPS() (See above). I try and keep most of the functions I create as variable as possible to service different situations, but everyone has their own preferences. Thank you for your suggestion though! I am currently updating a lot of the functions on this topic and am going to post a couple other round functions that are more compact.

@Beckett2000 - that sounds good

Function table.compare(tableA,tableB) - Compares Two Tables and returns true (same elements) or false (unequal elements)

In Lua, a table or function is only equal to itself, so in functions like indexOf (see above) even if you attempt to search for a table that has the exact same elements, you will get a false return because although the elements may be the same, they are not the same table. This function is a recursive comparison which means that it will evaluate every element in tables, and if there are sub tables it will go back over them and evaluate those elements as well. The return is true or false, which indicates if the tables are equal by contents or not equal.

I am considering merging this function with the indexOf function to make it more versatile as well, so would be interested to hear what people think of this.


-- Compares Two Tables Based on Elements
function table.compare(source,target)
    
    if type(source) ~= "table" then 
     error("Invalid argument no.1 to compare, table expected, was "..type(source))
    elseif type(target) ~= "table" then 
     error("Invalid argument no.2 to compare, table expected, was "..type(target)) end
    
    local function Compare(src,trg) -- Recursive Element Comparison
     for key,value in pairs(src) do 
      if trg[key] == nil and src[key] ~= nil then return false
      elseif trg[key] ~= value then 
        if type(trg[key]) == "table" and type(src[key]) == "table" then
         if Compare(src[key],trg[key]) == false then return false end
        else return false end end end
     return true end
    
    return Compare(source,target)
    
end

Function - string.normalize - Formats/Normalizes String Values

This function formats certain values in a string to output a more usable string. There are many possibilities for it, and it could also be modified to output syntax for other coding languages, but currently it has 4 options, Title,Upper,Lower, and Trim. I will. continue adding syntax to it if requested or I need to extend functionality for personal use, but currently the options above are the only that I have readily used.

Examples:

local String = " Testing foO 25 BaR. "

“Title” - Converts the words to a title cases (First Upper, Rest Lower)

string.normalize("Title",String) - Returns " Testing Foo 25 Bar. "

“Upper” - Converts the words to Upper Case

string.normalize("Upper",String) - Returns " TESTING FOO 25 BAR. "

“Lower” - Converts the words to Lower Case

string.normalize("Lower",String) - Returns " testing foo 25 bar. "

“Trim” - Trims Spaces from beginning and end of string

string.normalize("Trim",String) - Returns “Testing foO 25 BaR.”

Finally, you can use multiple types of normalization at once, though currently the only ones which are compatible are a combination of any of the word formats with the “Trim”, but this will likely expand as I add more options.

string.normalize("Trim Title",String) - Returns “Testing Foo 25 Bar.”

The function to normalize is below, and also the input types are automatically normalized themselves to a “Title” case, so if you used “title” or even “tiTlE” for “Title” it would still work, which should help for simple typing errors/ ease of use. Arguments are also order insensitive for type in addition to case insensitive.


-- Normalizes String Entries
function string.normalize(type,string)

 local Types = {} -- Stores Normalization Types
  for First,Rest in string.gmatch(type,"(%a)(%a+)") do 
  table.insert(Types,string.upper(First)..string.lower(Rest)) end
    
 local Output for _,Format in ipairs(Types) do
 if Format == "Title" then -- Sets Caps for Words in String (First Upper Rest Lower)
  Output = string.gsub(string,"(%a)(%a+)",function(a,b) return string.upper(a)..string.lower(b) end)
 elseif Format == "Upper" then -- Sets Words in String to all Uppercase
  Output = string.gsub(string,"(%a+)",function(a) return string.upper(a) end)
 elseif Format == "Lower" then -- Sets Words in String to all Lowercase
  Output = string.gsub(string,"(%a+)",function(a) return string.lower(a) end)
 elseif Format == "Trim" then -- Trims Spaces form Beginning and End of String
  local Str,Spaces = string.gsub(string,"(%s+)(.+)","%2"),{} for _ in string.gmatch(Str,"%s") do
   table.insert(Spaces,"%s-") end Output = string.gsub(Str,table.concat(Spaces).."$","")
 end end return Output

end