Automatic Localization

Hello.  I am hoping there is a Lua expert out there who might know how to do the following.

In my day to day coding, I often localize a number of Corona and Lua functions to achieve a speedup.

-- Loclized Lua and Corona Functions local mAbs = math.abs local mRand = math.random local getInfo = system.getInfo local getTimer = system.getTimer local strMatch = string.match local strFormat = string.format local pairs = pairs

While, this is easy and straightforward to do, it is time consuming and wasteful of file space.  i.e. Before my code begins, I have a bunch of localization statements.

I am hoping that Lua has some ‘magic’ means of localizing functions.  For example, and bear with me, I’m looking to simply replace the above example with something like this:

local localizer = require "localizer" localizer.doit()

The ‘localizer’ module would be defined something like this:

local localizeSomeFunctions() --[[Some LUA magic to localize: math.abs math.random system.getInfo ... etc. --]] end local public = {} public.doit = localizeSomeFunctions return public

So, what I’m looking for is the ‘Lua Magic’ in the above code. 

Thanks in advance to anyone who digs into this.  I’m open to solutions and/or hints, links, etc. that can lead me to a solution.  

I’m quite willing to do more research, but thus far I’ve had no luck with this.

This isn’t exactly what your after, but might serve as inspiration:

local mMath = {} for k, v in pairs( math ) do if type( v ) == "function" then mMath[k] = v end end -- usage mMath["random"]( 1, 50 ) -- or mMath.random( 1, 50 )

The bad side to this is that your still left with table lookups. So really the above isn’t any better than just doing:

local mMath = math

It’s a great question though and you have piqued my interest. Let me come back to this when i am back at my computer tomorrow.

Thanks for the idea.  As I accumulate more ideas, I’ll do a relative comparison for performances.

-Ed

Argh, I had something written up with more info and then my browser helpfully crashed on me.  :frowning:

“local” operates at compile-time, setting aside some slots, so you can’t make new ones from sandboxed code.

But this does come up a lot, say here: http://lua.2524044.n2.nabble.com/Suggestion-for-5-3-import-for-creating-a-quot-slew-quot-of-locals-td7654150i20.html Since Lua 5.3 isn’t even close to out the door those ideas should still be reasonably useful.

Hi. I do have a trick in my sleeve. I have a magic script that works in simulator, but fails on device. I need to debug it, but haven’t got a chance yet. I will allocate some time for it this week.

@StarCrunch,

I tried the top two code samples from that link with some success:

local function import( aName, ... ) local aModule = require( aName ) local aFunction = debug.getinfo( 2, 'f' ).func local \_, env = debug.getupvalue( aFunction, 1 ) for anIndex = 1, select( '#', ... ) do local aName = select( anIndex, ... ) env[aName] = aModule[aName] -- Failed here!! end end import( 'math', 'min', 'max' ) print( min, min( 1, 2 ) ) print( max, max( 1, 2 ) ) print( math ) 

This failed with an error on the line marked ‘Failed here!!’.    It seems the variable ‘env’ is nil.  I’m still puzzling this.

local function import( aName ) local aModule = require( aName ) local anIndex = 0 local aList = {} while true do anIndex = anIndex + 1 if debug.getlocal( 2, anIndex ) == nil then break end end for anIndex = anIndex - 1, 1, -1 do local aName, aValue = debug.getlocal( 2, anIndex ) if aModule[aName] == nil or aValue ~= nil then break end debug.setlocal( 2, anIndex, aModule[aName] ) end end local min, max, sqrt = nil; import( 'math' ); print( min, min( 1, 2 ) ) print( max, max( 1, 2 ) )

This  worked, and when tested for speed, this produced code with the same speed up of this:

local min = math.min local max = math.max local sqrt = math.sqrt

So, this is a little better as I can make my localization all on one line per library/source.  Now to find other options.

Thanks again!

OK.  I still want something, better, but this works for those who want to use it.  Do the following.

  1. Add this code into a module named “autolocalize.lua”

    local function go( module ) local mod = require( module ) local i = 1 while(debug.getlocal(2,i) ~= nil ) do local name,value = debug.getlocal(2,i) --print(name,value) if(mod[name] ~= nil) then --print(“Found”, name, mod[name] ) debug.setlocal(2,i,mod[name]) end i = i + 1 end end local public = {} public.go = go return public

  2. Use it like this:

    local min,max,sqrt,random; require(“autolocalize”).go(“math”) local getTimer,getInfo; require(“autolocalize”).go(“system”)

@Sergey, I hope you have a better idea!

Mine is similar, I have app.setLocals() function here https://gist.github.com/Lerg/8791421

I can write

[lua]

local _W, _H, _CX, _CY

app.setLocals()

[/lua]

But it’s not working on a device.

@Gremlin.Interactive (“Danny”),

I tried your idea out too and unfortunately, that was slower.  I was stymied for a moment, then I realized, we’re paying a penalty for the temporary string.  

Either way, you create a temporary string, which costs time:

-- usage mMath["random"]( 1, 50 ) -- or mMath.random( 1, 50 )

Bummer!

@Lerg (Sergey),

I like your solution better, because it lets me use custom spellings for my locals!

Here is the module:

local mMin,mMax,mSqrt = math.min,math.max,math.sqrt local function go() local locals = {mMin=mMin,mMax=mMax,mSqrt=mSqrt} local i = 1 repeat local k, v = debug.getlocal(2, i) if k then if v == nil then if not locals[k] then print('No value for a local variable: ' .. k) else debug.setlocal(2, i, locals[k]) end end i = i + 1 end until nil == k end local public = {} public.go = go return public

Here is the usage:

local mMin,mMax,mSqrt; require("autolocalize2").go("math")

This works on the simulator and on my device.

Notice, I made a slight change to your code.  See the first line of the module. Create custom locals in the module, then set them later in the calling file.

Cool! Both iOS and Android? I was testing on my iPad. Maybe corona folks changed something since then.

And you can move the locals table outside the function to save some cpu time.

Yes, it works on both my Nexus 7 and my iPad Air!

Thank You.

Good to know you wound up with something you like.

Looking over the mailing list link, the first case seems to be using Lua 5.2 (it mentions _ENV, for instance), so I’d guess it was just an incompatibility on account of the environment-related changes in the newer version.

On that other comment, accessing math.random… the string will have been interned at that point (actually, earlier, when the math library is loaded), so it ends up being a constant (just its hash, actually). However, the VM will still have to do lookup in mMath, versus using slightly cheaper bytecode that just reads out of a local slot directly.

I posted a new article talking about my final version of the above solution.  It includes source code and samples: http://roaminggamer.com/2014/07/08/auto-localization-trick-lua/

Also, I’ll be talking about it on the Corona Geek hangout today.

This isn’t exactly what your after, but might serve as inspiration:

local mMath = {} for k, v in pairs( math ) do if type( v ) == "function" then mMath[k] = v end end -- usage mMath["random"]( 1, 50 ) -- or mMath.random( 1, 50 )

The bad side to this is that your still left with table lookups. So really the above isn’t any better than just doing:

local mMath = math

It’s a great question though and you have piqued my interest. Let me come back to this when i am back at my computer tomorrow.

Thanks for the idea.  As I accumulate more ideas, I’ll do a relative comparison for performances.

-Ed

Argh, I had something written up with more info and then my browser helpfully crashed on me.  :frowning:

“local” operates at compile-time, setting aside some slots, so you can’t make new ones from sandboxed code.

But this does come up a lot, say here: http://lua.2524044.n2.nabble.com/Suggestion-for-5-3-import-for-creating-a-quot-slew-quot-of-locals-td7654150i20.html Since Lua 5.3 isn’t even close to out the door those ideas should still be reasonably useful.

Hi. I do have a trick in my sleeve. I have a magic script that works in simulator, but fails on device. I need to debug it, but haven’t got a chance yet. I will allocate some time for it this week.

@StarCrunch,

I tried the top two code samples from that link with some success:

local function import( aName, ... ) local aModule = require( aName ) local aFunction = debug.getinfo( 2, 'f' ).func local \_, env = debug.getupvalue( aFunction, 1 ) for anIndex = 1, select( '#', ... ) do local aName = select( anIndex, ... ) env[aName] = aModule[aName] -- Failed here!! end end import( 'math', 'min', 'max' ) print( min, min( 1, 2 ) ) print( max, max( 1, 2 ) ) print( math ) 

This failed with an error on the line marked ‘Failed here!!’.    It seems the variable ‘env’ is nil.  I’m still puzzling this.

local function import( aName ) local aModule = require( aName ) local anIndex = 0 local aList = {} while true do anIndex = anIndex + 1 if debug.getlocal( 2, anIndex ) == nil then break end end for anIndex = anIndex - 1, 1, -1 do local aName, aValue = debug.getlocal( 2, anIndex ) if aModule[aName] == nil or aValue ~= nil then break end debug.setlocal( 2, anIndex, aModule[aName] ) end end local min, max, sqrt = nil; import( 'math' ); print( min, min( 1, 2 ) ) print( max, max( 1, 2 ) )

This  worked, and when tested for speed, this produced code with the same speed up of this:

local min = math.min local max = math.max local sqrt = math.sqrt

So, this is a little better as I can make my localization all on one line per library/source.  Now to find other options.

Thanks again!