2010-12-11 13 views
5

Estoy empezando con Lua. En el ejemplo del que estoy aprendiendo (the Ghosts & Monsters Corona open source), veo este patrón repetidamente.¿Cuál es la diferencia entre estos dos ejemplos de Lua? ¿Es uno mejor?

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() 

Es esto algún tipo de convención de programadores con experiencia Lua recomiendan o no son verdaderas ventajas a hacerlo de esta manera? ¿Por qué no acaba de saltar la función todos juntos y hacer esto:

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") 

¿Hay algún beneficio implícito para el primer estilo sobre el segundo? ¡Gracias!

+0

¿Estás seguro de haberlo simplificado correctamente? ¿Podría vincular algunos de estos ejemplos en su forma completa? – Amber

+1

Sí, el 'patrón' que has publicado parece completamente sin sentido. ¿Dice que ve ese "mucho", puede vincular a un ejemplo? – Mud

+0

He editado la pregunta con un ejemplo más completo y he citado donde puede descargar el archivo fuente completo. Si Lua es como otros idiomas que conozco, parece que la segunda forma hace más con menos. –

Respuesta

8

¿Es esto algún tipo de convención con experiencia que los programadores de Lua recomiendan o hay ventajas genuinas para hacerlo de esta manera?

No es típico. La ventaja es que el estado del objeto es privado, pero eso no es suficiente como para recomendarlo.

Veo este patrón repetidamente.

Nunca lo he visto antes, y solo ocurre una vez en la fuente que ha publicado.

EDITAR: Agregar una respuesta a una pregunta formulada en los comentarios debajo de esta publicación.

Una función que accede a las variables locales externas enlaza a esas variables y se denomina 'cierre'. Lua (por razones históricas) se refiere a esas variables vinculadas como 'valores ascendentes'. Por ejemplo:

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 y b están obligados a cierres diferentes copias de i, como se puede ver en la salida. En otras palabras, puede pensar en un cierre como función con su propio estado privado. Usted puede usar esto para simular objetos:

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 devuelve una tabla de cierres, cada uno con destino a los locales y xy. La tabla no contiene el estado del punto, los cierres sí lo hacen, a través de sus valores ascendentes. Un punto importante es que cada vez que se llama Point crea nuevos cierres, lo que no es muy eficiente si tiene grandes cantidades de objetos.

Otra forma de crear clases en Lua es crear funciones que toman un cuadro como el primer argumento, con el estado de ser almacenados en la tabla:

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) 

Hasta el momento, todavía estamos creando nuevas copias de cada método, pero ahora que no estamos confiando en upvalues ​​para el estado, podemos arreglar eso:

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 

ahora los métodos se crean una vez, y todos los casos Point compartir los mismos cierres. Una mejor manera de hacer esto es utilizar las instalaciones metaprogramming de Lua para hacer nuevos Point casos se ven automáticamente en PointClass por métodos no se encuentran en la propia instancia:

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) 

Esta es una forma más idiomática de la creación de clases en Lua. Es más eficiente en cuanto a la memoria y más flexible (en particular, facilita la implementación de la herencia).

+0

Barro, aparece una vez en ese archivo en particular (main.lua) pero el autor utiliza el patrón en todo el código. Muchos de los archivos en el archivo (level * .lua, load * .lua,) hacen uso de este patrón. Dicho esto, director.lua, que está incluido en el archivo pero fue escrito por un autor diferente, no parece tener el patrón, lo que me lleva a creer que es el estilo de persona autor de Ghosts & Monster. –

+0

Sí, es lo suyo. El código en general no es muy idiomático Lua. En particular, su método de creación de clases es muy inusual: crear un nuevo cierre para cada método de instancia, almacenar estado en valores ascendentes, en lugar de un conjunto de métodos compartidos para la clase con estado almacenado en tablas. Esto es bastante inútil, pero también es una forma de crear un estado verdaderamente privado. Eso * podría * ser lo que está haciendo, pero luego hay otras áreas del código donde usa tablas de estado (a veces con la misma clase almacenando parte de su estado en valores ascendentes, y parte en una tabla). – Mud

+0

Lodo, ​​gracias por la discusión. Nuevamente, dado que soy nuevo en Lua, ¿te importaría explicar a qué te refieres con valores ascendentes? ¡Gracias! –

0

No veo mucho que apunte al primer estilo como lo has mostrado. Pero si dice algo como if arg then main() end en la parte inferior, la secuencia de comandos podría (simplemente podría) ser útil como una "biblioteca" cargable, además de ser una secuencia de comandos independiente. Dicho esto, tener un main() le gusta a C, no a Lua; Creo que tienes razón para cuestionarlo.

3

Con frecuencia escribo mis propios scripts Lua esta manera, ya que mejora la legibilidad en este caso:

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() 

De esta manera el código "principal" es en la parte superior, pero todavía puedo definir las funciones globales necesarias antes se ejecuta.

Un truco simple, de verdad. No puedo pensar en ninguna razón para hacer esto además de la legibilidad.

1

El primer estilo se puede usar también para mejorar la legibilidad, pero prefiero dar a la función un nombre significativo en lugar de principal o simplemente pasar sin la función.

Por cierto, creo que siempre es una buena práctica nombrar bloques de código, es decir, ponerlos en funciones o métodos. Ayuda a explicar tu intención con ese fragmento de código y fomenta la reutilización.

Cuestiones relacionadas