我刚刚开始使用Lua。在我从(the Ghosts & Monsters Corona open source)学习的例子中,我反复看到这种模式。
local director = require("director")
local 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()
这是Lua程序员推荐的某种惯例,或者这样做是否有真正的优势?为什么你不能一起跳过这个功能并执行此操作:
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" )
第二种风格对第二种风格有一些隐含的好处吗?谢谢!
答案 0 :(得分:8)
这是Lua程序员推荐的某种惯例,或者这样做是否有真正的优势?
这不典型。优点是对象状态是私有的,但推荐它是不够的。
我反复看到这种模式。
我之前从未见过它,它只在您发布的来源中发生过一次。
编辑:在本帖子下面的评论中添加对问题的回复。
访问外部局部变量的函数将绑定到这些变量,称为“闭包”。 Lua(由于历史原因)将那些绑定变量称为“upvalues”。例如:
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
和b
是绑定到i
的不同副本的闭包,正如您可以从输出中看到的那样。换句话说,您可以将闭包视为具有自身私有状态的函数。您可以使用它来模拟对象:
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
会返回一个闭包表,每个闭包都绑定到本地x
和y
。该表不包含点的状态,闭包本身通过它们的upvalues。重要的一点是,每次调用Point
时,它都会创建 new 闭包,如果你有大量的对象,效率不高。
在Lua中创建类的另一种方法是创建将表作为第一个参数的函数,其中状态存储在表中:
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)
到目前为止,我们仍然在创建每个方法的新副本,但是现在我们不依赖于upvalues for state,我们可以解决这个问题:
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
现在,方法创建一次,所有Point
个实例共享相同的闭包。更好的方法是使用Lua的元编程工具使新的Point
实例自动查找PointClass
中实例本身未找到的方法:
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)
这是在Lua中创建类的更惯用的方法。它的内存效率更高,更灵活(特别是它可以很容易地实现继承)。
答案 1 :(得分:3)
我经常以这种方式编写自己的Lua脚本,因为在这种情况下它提高了可读性:
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()
这样“主”代码位于顶部,但我仍然可以在执行之前定义必要的全局函数。
一个简单的伎俩,真的。除了可读性之外,我想不出任何理由这样做。
答案 2 :(得分:1)
第一种风格可以用得太可读了,但我宁愿给这个函数一些有意义的名字,而不是 main ,或者只是没有这个功能。
顺便说一句,我认为命名代码块总是一个好习惯,即将它们放入函数或方法中。它有助于解释您对该段代码的意图,并鼓励重用。
答案 3 :(得分:0)
我没有看到你所展示的第一种风格。但如果它在底部说了if arg then main() end
之类的东西,那么除了作为一个独立的脚本之外,该脚本可能(只是可能)可用作可加载的“库”。也就是说,有一个main()
那样的C,而不是Lua;我认为你提出质疑是正确的。