开发者

What's the difference between these two Lua examples? Is one better?

I'm just getting started with Lua. In the example I'm learning from (the Ghosts & Monsters Corona open source), I see this pattern repeatedly.

local director = require("director")

loca开发者_开发问答l mainGroup = display.newGroup()

local function main()

   mainGroup:insert(director.directorView)

   openfeint = require ("openfeint")
   openfeint.init( "App Key Here", "App Secret Here", "Ghosts vs. Monsters", "App ID Here" )

   director:changeScene( "loadmainmenu" )

   return true
end

main()

Is this some sort of convention experienced Lua programmers recommend or are there genuine advantages to doing it this way? Why wouldn't you just skip the function all together and do this:

local director = require("director")

local mainGroup = display.newGroup()

mainGroup:insert(director.directorView)

local openfeint = require ("openfeint")
openfeint.init( "App Key Here", "App Secret Here", "Ghosts vs. Monsters", "App ID Here" )

director:changeScene( "loadmainmenu" )

Is there some implicit benefit to the first style over the second? Thanks!


Is this some sort of convention experienced Lua programmers recommend or are there genuine advantages to doing it this way?

It's not typical. The advantage is that object state is private, but that's not enough of an advantage to recommend it.

I see this pattern repeatedly.

I've never seen it before, and it happens only once in the source you posted.

EDIT: Adding a response to a question asked in the comments below this post.

A function which accesses external local variables binds to those variables and is called a 'closure'. Lua (for historical reasons) refers to those bound variables as 'upvalues'. For example:

local function counter()
   local i = 1
   return function()
      print(i)
      i = i + 1
   end
end

local a, b = counter(), counter()
a() a() a() b() --> 1 2 3 1

a and b are closures bound to different copies of i, as you can see from the output. In other words, you can think of a closure as function with it's own private state. You can use this to simulate objects:

function Point(x,y)
   local p = {}
   function p.getX() -- syntax sugar for p.getX = function()
      return x
   end
   function p.setX(x_)
      x = x_
   end
   -- for brevity, not implementing a setter/getter for y
   return p
end

p1 = Point(10,20)
p1.setX(50)
print(p1.getX())

Point returns a table of closures, each bound to the locals x and y. The table doesn't contain the point's state, the closures themselves do, via their upvalues. An important point is that each time Point is called it creates new closures, which is not very efficient if you have large quantities of objects.

Another way of creating classes in Lua is to create functions that take a table as the first argument, with state being stored in the table:

function Point(x,y)
   local p = {x=x,y=y}
   function p:getX() -- syntax sugar for p.getX = function(self)
      return self.x
   end
   function p:setX(x)
      self.x = x
   end
   return p
end

p1 = Point(10,20)
p1:setX(50) -- syntax sugar for p1.setX(p1, 50)
print(p1:getX()) -- syntax sugar for p1.getX(p1)

So far, we're still creating new copies of each method, but now that we're not relying on upvalues for state, we can fix that:

PointClass = {}
function PointClass:getX() return self.x end
function PointClass:setX(x) self.x = x end
function Point(x,y)
   return {
      x = x,
      y = y,
      getX = PointClass.getX,
      setX = PointClass.getY,
   }
end

Now the methods are created once, and all Point instances share the same closures. An even better way of doing this is to use Lua's metaprogramming facility to make new Point instances automatically look in PointClass for methods not found in the instance itself:

PointClass = {}
PointClass.__index = PointClass -- metamethod
function PointClass:getX() return self.x end
function PointClass:setX(x) self.x = x end
function Point(x,y)
   return setmetatable({x=x,y=y}, PointClass)
end

p1 = Point(10,20)
-- the p1 table does not itself contain a setX member, but p1 has a metatable, so 
-- when an indexing operation fails, Lua will look in the metatable for an __index
-- metamethod. If that metamethod is a table, Lua will look for getX in that table,
-- resolving p1.setX to PointClass.setX.
p1:setX(50)

This is a more idiomatic way of creating classes in Lua. It's more memory efficient and more flexible (in particular, it makes it easy to implement inheritance).


I frequently write my own Lua scripts this way because it improves readability in this case:

function main()
    helper1( helper2( arg[1] ) )
    helper3()
end

function helper1( foo )
    print( foo )
end

function helper2( bar )
    return bar*bar
end

function helper3()
    print( 'hello world!' )
end

main()

This way the "main" code is at the top, but I can still define the necessary global functions before it executes.

A simple trick, really. I can't think of any reason to do this besides readability.


The first style could be used too improove readability, but I would rather give the function some meaningful name instead of main or just go without the function.

By the way, I think it's always a good practice to name blocks of code, i.e. put them into functions or methods. It helps explain your intend with that piece of code, and encourages reuse.


I don't see much point to the first style as you've shown it. But if it said something like if arg then main() end at the bottom, the script might (just might) be useful as a loadable "library" in addition to being a standalone script. That said, having a main() like that smacks of C, not Lua; I think you're right to question it.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜