Lua_ 14th Chapter Packages

Source: Internet
Author: User
Tags define local lua mul

Chapter VI Packages

Many languages specialize in providing a mechanism for naming global variables, such as Modula's Modules,java and Perl's packages,c++ namespaces. Each mechanism has different rules for the visibility of the elements declared in the package and the use of some other details. But they all provide a mechanism to avoid problems with naming conflicts in different libraries. Each library creates its own namespace, and the names defined in this namespace do not interfere with the names defined in other namespaces.

LUA does not provide a clear mechanism to implement packages. However, the basic mechanisms that we provide through language are easy to achieve with him. The main idea is to use a table to describe the package like a standard library.

The obvious benefit of using tables to implement packages is that we can use packages like any other table, and we can use all the features provided by the language to bring a lot of convenience. In most languages, packages is not a first-class value (first-class values) (that is, they cannot be stored in variables, not as function arguments ...). As a result, these languages require special methods and techniques to achieve similar functionality.

Lua, although we have been using tables to implement Pachages, there are different ways to implement the package, which we'll cover in this chapter.


14.1 Basic Methods

The simple approach to the first package is to prefix each object in the package with a package name. For example, suppose we are writing a library that operates on complex numbers: we use tables to represent complex numbers, the table has two fields R (real number) and I (imaginary part). We declare all of our operations in another table to implement a package:

Complex = {}  function complex.new (r, i) return {r=r, i=i} end--defines aconstant ' i ' complex.i =complex.new (0, 1) 
   
    function Complex.add (c1, C2) return complex.new (C1.R + c2.r,c1.i + c2.i) End  function complex.sub (c1, C2) return compl Ex.new (c1.r-c2.r,c1.i-c2.i) End Function Complex.mul (c1, C2) return complex.new (c1.r*c2.r-c1.i*c2.i, c1.r*c2.i +c1.i* C2.R) End  function Complex.inv (c) Local n = c.r^2+ c.i^2return complex.new (c.r/n,-c.i/n) end  return complex
   

This library defines a global name: Coplex. All other definitions are placed in this table. With the definition above, we can use any complex operation that conforms to the specification, such as:

c = Complex.add (complex.i, Complex.new (10, 20))

This use of the table to implement the package and real package functionality is not exactly the same. First, we must display the name of the package in front of each function definition. Second, the functions within the same package call each other must specify the package name before the function is called. We can use the local variable name of the state to improve the problem, and then assign the local variable to the final package. Based on this principle, we rewrite the above code:

Local P = {}complex = P       --package namep.i ={r=0, I=1}function p.new (R, i) return {r=r, i=i} endfunction p.add (c1, C2 )       return p.new (C1.R + c2.r,c1.i + c2.i) End ...

When a function within the same package calls another function (or she calls itself), he still needs to add the prefix name. At the very least, it is no longer dependent on the state's package name. In addition, only one place needs the package name. You may notice the last statement in the package:

Return complex

This return statement is not required because the package has already been assigned to the global variable complex. However, we think it is a good habit to return itself when the package is opened. The extra return statement does not cost much and provides an alternative way to manipulate the package.


14.2 Private members (Privacy)

Sometimes, a package exposes all of his content, which means that any client of the package can access him. However, it is also useful for a package to have its own private part (that is, only the package itself can be accessed). A traditional method in Lua is to define the private part as a local variable to implement. For example, we modify the example above to increase the private function to check whether a value is a valid complex number:

Local P = {} complex = Plocal function Checkcomplex (c)    if not ((type (c) = = "Table") and    Tonumber (C.R) and Tonumbe R (C.I.) then          error ("Bad complex Number", 3)     endend  function p.add (c1, C2)    Checkcomplex (C1);   Checkcomplex (C2);   Return p.new (C1.R + c2.r,c1.i + c2.i) end ...  Return P

What are the advantages and disadvantages of this approach? Pros : All the names in the package are in a separate namespace. Each entity in a package is clearly marked as public or private. In addition, we implement a true privacy (privacy): Private entities are inaccessible outside the package. Disadvantage: It is redundant to access other public entities within the same package and must be prefixed with P. Another big problem is that when we modify the state of a function (public to private or private to public), we have to modify the way the function is called.

There is an interesting way to solve both of these problems at once. We can declare all functions within the package as local, and finally put them in the final table. In this way, the complex package above is modified as follows:

Local function Checkcomplex (c)     if not ((type (c) = = "Table") and     Tonumber (C.R) and Tonumber (CI)) then          error ( "Bad complex number", 3)     EndEnd local function new (R, i) return {r=r, i=i} endlocal functionadd (c1, C2)   checkcom Plex (C1);   Checkcomplex (C2);    return new (C1.R + c2.r,c1.i + c2.i) End  ... complex = {        new =new,         add = Add,         sub= Sub,          mul =mul,
   div = Div,}

Now that we no longer need to invoke the function, we prefix it with the same method of public and private function invocation . At the end of the package, there is a simple list that lists all public functions. Most people may find this list more natural at the beginning of the package, but we can't do that because we have to define local functions first.

14.3 Packages and files

We often write a package and then put all the code in a separate file. Then we just need to execute this file to load the package. For example, if we put the package code for our complex number above in a file Complex.lua, the command "Require complex" will open the same. Remember that the Require command does not load the same package multiple times.

The question to be aware of is that the relationship between the file name of the package and the package name is kept clear. Of course, it's a good idea to connect them because the Require command uses files instead of packages. One workaround is to name the file by adding a suffix (such as. Lua) after the package. LUA does not require a national extension, but is determined by your path settings. For example, if your path contains: "/usr/local/lualibs/?". LUA, the complex package may be saved in a Complex.lua file.

Some people prefer to name the package before naming it. In other words, if you rename the file, the package is renamed. This solution provides a great deal of flexibility. For example, if you have two package with the same name, you don't need to modify any one, just rename the file. In Lua, we use

_requiredname variable to rename. Remember, when require loads a file, it defines a variable to represent the virtual file name. Therefore, you can write this in your package:

Local P = {}      --packageif _requiredname = = Nil then    complex = Pelse     _g[_requiredname] = Pend

The if test in the code allows us to use the package without require. If _requiredname is not defined, we use the national name to represent the PACKAGEC example complex). In addition, thepackageuses the virtual file name to register himself. If you place the library in the file Cpx.lua with the consumer and run require CPX, the package loads itself into the table CPX. If other users rename the library to Cpx_v1.lua and run require CPX_V1, then the package will automatically load itself into the table CPX_V1.

14.4 Using Global Tables

The downside of these methods of creating the package is that they require programmers to pay attention to a lot of things, such as making it easy to forget the local keyword when declaring it. The metamethods of the global variables table provides some interesting techniques and can also be used to implement the package. What's common in these technologies is that the package uses an exclusive environment. This is easy to achieve: if we change the environment of the package master chunk, all the functions created by the package share this new environment.

The simplest technical implementation. Once the package has an exclusive environment, not only does all of her functions share the environment, but all of its global variables also share the environment. So, we can declare all the public functions as global variables, and then they will automatically exist as separate tables (the name of the package), and all the package must do is register the table as the package name. The following code illustrates the results of using this technique in complex libraries:

Local P = {} complex = P setfenv (1, p)

Now, when we declare the function add, she will automatically become complex.add:

function Add (c1, C2)    return new (C1.R + c2.r,c1.i + c2.i) end

in addition, we can call other functions in this package without the need for a prefix. For example, the Add function calls the new function, and the environment is automatically converted to complex.new. This approach provides good support for the package: programmers rarely need to do extra work, calling functions within the same package without a prefix, and calling public and private functions is no different. If the programmer forgets the local keyword, it does not pollute the global namespace, but makes the private function a public function. In addition, we can combine this technique with the method of the package name we used in the previous section:

Local P = {}      --Packageif _requiredname = Nil then    complex = Pelse    _g[_requiredname] = pendsetfenv (1, P)

This makes it impossible to access other packages. Once we have an empty table p as our environment, we lose access to all previous global variables. There are several ways to solve this problem, but each has its pros and cons. The simplest solution is to use inheritance, as we saw earlier:

Local P = {}      --Package setmetatable (p, {index =_g}) setfenv (1, p)

(You have to call Setmetatable before calling Setfenv, can you tell why?) With this structure, the package can access all the global identifiers directly, but must pay a small price for each access. Theoretically, this workaround brings an interesting result: Your package now contains all the global variables. For example, using your package person can also invoke the SIN function of the standard library: Complex.math.sin (x). (This feature is also available in the Perl ' s package system)

Another way to quickly access other packages is to declare a local variable to hold the old environment:

<pre name= "code" class= "CSharp" >local p = {} pack= p local _g = _g setfenv (1, p)

now you have to prefix the external access with _g., but the access speed is faster because this does not involve metamethod. Unlike inheritance, this approach allows you to access the old environment, and the good and bad of this approach is controversial, but sometimes you may need this flexibility. A more formal approach is to declare only the functions or packages you need as Local:

Local p = {} pack= P--Import section:  --Declare everything the package needsfrom outsidelocal sqrt = Math.sqrtloca L IO = io--no moreexternal access Afterthis pointsetfenv (1, P) <span style= "FONT-SIZE:14PX; Font-family:arial, Helvetica, Sans-serif; Background-color:rgb (255, 255, 255); " > </span>

This technique requires a little more, but he makes your package more independent. He is also faster than the previous methods.

14.5 Other-some tricks (other Facilities)

as I said earlier, using a table to implement all the powerful features of Lua that can be used in the packages process. There are infinite possibilities in this. Recommendation: We do not need to put together the definitions of all public members of the package, for example, we can add a new function to our complex package in a separate chunk:

function Complex.div (c1, C2)   return Complex.mul (C1,COMPLEX.INV (C2)) end

(But note: Private members must be confined to a file, which I think is a good thing) in turn, we can define multiple packages within the same file, all we need to do is put each package in a do block, so that local The variable can be limited to that block of code.

Outside of the package, if we need to use a function frequently, we can define a local variable name for them:

Local add, I =complex.add, complex.i C1 =add (Complex.new (Ten), i)

If we don't want to rewrite the package name over and over again, we use a short local variable to represent the package:

Local C = Complexc1 =c.add (c.new (Ten), C.I.)

It is also easy to write a function to unpack the package, putting all the names in the package into the global namespace:

function OpenPackage (NS) for n,v in pairs (ns) do _g[n] = v endend openpackage (complex) C1 =mul (New (ten), i)


If you're worried about naming conflicts when you open the package, check to see if the name exists before assigning a value:

function OpenPackage (NS) for      n,v in pairs (ns) does          if _g[n] ~= nil then              error ("name Clash:"). N.. "Is alreadydefined")          end          _g[n] = v      endend

Since packages itself is also a table, we can even nest packages in packages, which means that we can also create a package within a suite, and it is seldom necessary to do so.

Another interesting thing is the automatic loading: functions are automatically loaded only when they are actually used. When we load an automatically loaded package, a new empty table is automatically created to represent the package and the index Metamethod of the table is set to complete the automatic loading. When we call any function that is not loaded,

Indexmetamethod will be triggered to load a function. When the call discovery function has been loaded, index will not be triggered.

Here's a simple way to implement automatic loading. Each function is defined in a secondary file. (There may also be multiple functions within a file) each of these files defines a function in a standard way, for example:

function Pack1.foo () ... end  function Pack1.goo () ... end

However, the file does not create a package because it exists when the function is loaded. In the main package we define a secondary table to record where the function is stored:

Local location = {    Foo = "/usr/local/lua/lib/pack1_1.lua",     goo = "/usr/local/lua/lib/pack1_1.lua",     foo1 = " /usr/local/lua/lib/pack1_2.lua ",     goo1 ="/usr/local/lua/lib/pack1_3.lua ",}

Below we create the package and define her metamethod:

</pre></div><pre name= "code" class= "CSharp" >pack1 = {}setmetatable (pack1, {index =function (t, funcname) Local file = Location[funcname]if not file then    error (' Package pack1 doesnot define '. funcname) Endassert (loadfile (file)) ()    --Loadand run definition return t[funcname]                            --returnthe function End}) Return Pack1

After the package is loaded, the first time the program executes Pack1.foo () will trigger index Metamethod, and then find that the function has a corresponding file and loads the file. The subtlety is that the file is loaded and the function is returned as the result of the access.

Because the entire system is written using Lua, it is easy to change the behavior of the system. For example, a function can be written in C and loaded with Loadlib in Metamethod. Or we can set a metamethod from the global table to load the entire packages. There are infinite possibilities for you to discover.

Lua_ 14th Chapter Packages

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.