Modules (module) and package (package) in Lua are detailed _lua

Source: Internet
Author: User
Tags function definition lua

Objective

Starting with the Lua5.1 version, new support is added to modules and packages, but modules and packages are defined and used using require and module. Require is used to use modules, and module is used to create modules. Simply put, a module is a library of programs that can be loaded by require. Then we get a global variable that represents a table. This table is like a namespace with everything that is exported in the module, such as functions and constants, and a conforming module should also enable require to return to the table. Now let's make a concrete summary of the two functions, require and module.

Require function

LUA provides a function called require to load a module. To load a module, simply call require "< module name >". This call returns a table consisting of a module function and also defines a global variable that contains the table. However, these behaviors are done by the module, not by the require. Therefore, some modules will choose to return other values or have other effects. So how exactly does require load the module?

First, to load a module, you have to know where the module is. Know where the module is, before it can be loaded properly. How does Lua find this mod when we write code like require "mod"? Here is said, I will say in detail here.

When searching for a file, in Windows, many are based on the Windows environment variable path to search, and require use the same path as the traditional path, require the path is a series of patterns, each of which is a way to convert the module name to the file name. Require will replace each with the module name? And then checks for the existence of such a file based on the result of the substitution, and if it does not exist, the next item is tried. Each entry in the path is separated by a semicolon, such as a path with the following string:

Copy Code code as follows:

?;?. lua;c:\windows\?;/ Usr/local/lua/?/? Lua

Then, when we require "mod", we try to open the following file:

Copy Code code as follows:

MoD
Mod.lua
C:\windows\mod
/usr/local/lua/mod/mod.lua

As you can see, the Require function only handles semicolons and greetings, and the rest is defined by the path itself. In practical programming, the path of the LUA file require for searching is stored in the variable Package.path, and on my Computer, print (Package.path) prints the following:

Copy Code code as follows:

;. \?. Lua;d:\lua\5.1\lua\? Lua;d:\lua\5.1\lua\?\init.lua;d:\lua\5.1\? Lua;d:\lua\5.1\?\init.lua;d:\lua\5.1\lua\? Luac

If require cannot find a LUA file that matches the module name, LUA will start looking for the C library, which is the address of Package.cpath, and on my Computer, print (Package.cpath) outputs the following values:

Copy Code code as follows:

.\?. DLL;. \?51.dll;d:\lua\5.1\? Dll;d:\lua\5.1\?51.dll;d:\lua\5.1\clibs\? Dll;d:\lua\5.1\clibs\?51.dll;d:\lua\5.1\loadall.dll;d:\lua\5.1\clibs\loadall.dll

When this file is found, if the file is a LUA file, it loads the file via LoadFile, and if a C library is found, it is loaded by loadlib. LoadFile and loadlib all just loaded the code and didn't run them, and in order to run the code, require would invoke the module name as a parameter. What if the Lua file and the C program library are not found? Let's try and require one thing, like:

Copy Code code as follows:

Require "Jellythink"
Lua:test.lua:1: module ' jellythink ' not found:
No field package.preload[' Jellythink ']
No file '. \jellythink.lua '
No file ' D:\Lua\5.1\lua\jellythink.lua '
No file ' D:\Lua\5.1\lua\jellythink\init.lua '
No file ' D:\Lua\5.1\jellythink.lua '
No file ' D:\Lua\5.1\jellythink\init.lua '
No file ' D:\Lua\5.1\lua\jellythink.luac '
No file '. \jellythink.dll '
No file '. \jellythink51.dll '
No file ' D:\Lua\5.1\jellythink.dll '
No file ' D:\Lua\5.1\jellythink51.dll '
No file ' D:\Lua\5.1\clibs\jellythink.dll '
No file ' D:\Lua\5.1\clibs\jellythink51.dll '
No file ' D:\Lua\5.1\loadall.dll '
No file ' D:\Lua\5.1\clibs\loadall.dll '

Yes, there will be an error. The above is the general workflow of require.

The skill of the odd sexual

As you can see, these are all summarized by using the name of the module. But sometimes it is necessary to rename a module to avoid name collisions. For example, there is a scenario where you need to load different versions of the same module in a test and get a performance difference between versions. So how do we load different versions of the same module? For a LUA file, we can easily get rid of its name, but for a C library, there's no way to edit the name of the luaopen_* function. For this kind of renaming, require has a little trick: If a module name contains hyphens, require creates luaopen_* function names with the hyphen content. For example: If the name of a module is A-b,require, it will assume that its open function is named Luaopen_b, not luaopen_a-b. Well now, the need for testing the different versions presented above can be solved.

Write a module of our own

The easiest way to create a module in LUA is to create a table and put all the functions that need to be exported into it, and then return to this table. Equivalent to the exported function as a field in the table, in Lua the function is the first class value, providing a natural advantage. To write a module of our own, the code is as follows:

Copy Code code as follows:

Complex = {}-global variable, module name

function Complex.new (r, i) return {R = r, i = i} end

--Define a constant 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 Complex.new (C1.R-C2.R, c1.i-c2.i)
End

Return complex--Returns the module's table

The top is the simplest module. In the process of writing code, you'll find that you have to explicitly place the module name in each function definition, and a function must qualify the name of the called function when it calls another function in the same module, but we can work around to define a partial table-type variable in the module. This local variable defines and invokes the function within the module, and then assigns the local name to the final name of the module, as follows:

Copy Code code as follows:

Local M = {}--Partial variables
Complex = M--assigns this local variable to the module name eventually

function M.new (r, i) return {R = r, i = i} end

--Define a constant I
M.I = m.new (0, 1)

function M.add (c1, C2)
Return m.new (C1.R + C2.R, c1.i + c2.i)
End

function M.sub (c1, C2)
Return M.new (C1.R-C2.R, c1.i-c2.i)
End

Return complex--Returns the module's table

In this way, we actually use a local variable inside the module. This looks simple and rough, but each function still needs a prefix. In fact, we can avoid writing the module name altogether, because require will pass the module name as a parameter to the module. Let's do a trial:

Copy Code code as follows:

Local ModuleName = ...

--Printing parameters
For i = 1, select (' # ', ...) do
Print (select (i, ...))
End

Local M = {}--Partial variables
_g[modulename] = M--assign this local variable to the module name eventually
Complex = M

function M.new (r, i) return {R = r, i = i} end

--Define a constant I
M.I = m.new (0, 1)

function M.add (c1, C2)
Return m.new (C1.R + C2.R, c1.i + c2.i)
End

function M.sub (c1, C2)
Return M.new (C1.R-C2.R, c1.i-c2.i)
End

Return complex--Returns the module's table

Save the above code as Test1.lua. Then write a file with the following code:

Copy Code code as follows:

Require "test"

C1 = test.new (0, 1)
C2 = test.new (1, 2)

RET = Test.add (c1, C2)
Print (RET.R, ret.i)

Save the above code as Test2.lua

Place the above code under the same folder, run the Test2.lua file, and print the results as follows:

Copy Code code as follows:

Test1
1 3

(PS: If you have three dots in your code (...) Unfamiliar students, please refer to the "functions in Lua", after this modification, we can not define the module name in the module, if you need to rename a module, you can only rename the file that defines it.

Careful classmate may notice the end of the module return statement, such a return statement, in the definition of modules, is very easy to write, how to do? It would be better if all of the setup tasks associated with the module were centered at the beginning of the module. One way to eliminate the return statement is to assign the module table directly to package.loaded, as follows:

Copy Code code as follows:

Local ModuleName = ...

Local M = {}--Partial variables
_g[modulename] = M--assign this local variable to the module name eventually

Package.loaded[modulename] = M
--Subsequent code omitted

Sample code Download: Click here to download

What is package.loaded?

Require stores the return value in the table package.loaded, and if the loader does not return a value, require returns the value in table package.loaded. As you can see, in our code above, the module does not return a value, but instead directly assigns the module name to the table package.loaded. What does that mean, package.loaded? This table holds all the modules that have been loaded. Now we can see how the Require is loaded?

1. First Judge package.loaded this table has no corresponding module information;
2. If there is, directly return the corresponding module, no longer for the second load;
3. If not, load, return the loaded module.

Say "Environment".

As you may notice, when I access other functions in the same module, I need to qualify the name, like the m in the above code. When I change a local function within a module from private to public, the corresponding call to the locally function needs to be modified, plus the qualified name. What to do? You can't always change the code every time. How to get it done at once? Do you remember the "Environment concept in LUA" blog, where the concept of the environment can be useful here.

We can make the module's main program block have an exclusive environment, not only does all of its functions share the table, but all its global variables are also recorded in this table, and all public functions can be declared as global variables so that they are automatically recorded in a separate table. What the module has to do is give the table the module name and package.loaded. For example, the following code can be completed:

Copy Code code as follows:

Local ModuleName = ...

Local M = {}--Partial variables
_g[modulename] = M--assign this local variable to the module name eventually

Package.loaded[modulename] = M
Setfenv (1, M)

After that, when we write down the following code:

Copy Code code as follows:

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

It is actually equivalent to the following code:
Copy Code code as follows:

function M.add (c1, C2)
Return m.new (C1.R + C2.R, c1.i + c2.i)
End

When I call a function new in the same module, I don't have to specify M. This allows us to omit the prefix when writing our own modules, as well as other benefits that you can think about yourself. However, after we call setfenv, an empty table m is used as an environment, and we cannot access global variables in the previous environment. What's the best way to do that? Several methods are now available.

Method One:

The easiest way to do this is to use the meta table, set __index, and simulate inheritance, as stated in the concept of environment in LUA. The code is as follows:

Copy Code code as follows:

Local ModuleName = ...

Local M = {}--Partial variables
_g[modulename] = M--assign this local variable to the module name eventually

Package.loaded[modulename] = M

Setmetatable (M, {__index = _g})
Setfenv (1, M)

The above code is very simple, the principle in the previous blog has been detailed, here no longer verbose. Because of the need to set the meta table, all have a certain cost, but can be ignored.

Method Two:

Copy Code code as follows:

Local ModuleName = ...

Local M = {}--Partial variables
_g[modulename] = M--assign this local variable to the module name eventually

Package.loaded[modulename] = M

Local _g = _g--saving global environment variables
Setfenv (1, M)

So in your own module to save a global environment variable, when we visit the previous environment variables, we need to add prefix _g, seemingly a little trouble. However, because the Meta method is not involved, this method is slightly faster than the method.

Method Three:

This is the most formal way to declare a function or module that needs to be used as a local variable, looking at the following code:

Copy Code code as follows:

Local ModuleName = ...

Local M = {}--Partial variables
_g[modulename] = M--assign this local variable to the module name eventually

Package.loaded[modulename] = M

Local sqrt = math.sqrt-we need to use the MATH.SQRT function in our own module, so we save it first
Local IO = io-need to use IO Library, also save
Setfenv (1, M)--After the setup is complete, you can no longer use the contents of the _g table

Method three needs to do most of the work, but also the most troublesome, but the performance is the best. How to use, you see do it yourself.

module function

As you may have noticed, when you define a module, the preceding code is the same, and it is divided into the following steps:

1. Get the module name from the parameters passed in require;
2. Create an empty table;
3. Add the field to the module name in the Global environment _g, and assign the null table to this field;
4. Set the module in the loaded table;
5. Set environment variables.

This is a couple of steps that need to be added before each module is defined, and it's not a bit of a hassle to provide a new function module in Lua5.1 that includes the functionality that these steps accomplish. When writing a module, you can replace the previous setup code directly with the following code:

Copy Code code as follows:

Module (...)

For the above code, it creates a new table and assigns it to the global field and loaded table for the module name, and finally sets the table as the environment for the main program block. By default, module does not provide external access, that is, you do not have access to the previous environment, in the "Environment" section, I specifically said three kinds of solutions. This is done in the case of module use:
Copy Code code as follows:

Module (..., package.seeall)

The function of this sentence is like the previous function plus setmetatable (M, {__index = _g}). With this code, basically it can be said that everything is not worried.

Sub modules and Packages

LUA supports hierarchical module names, and you can use a single point to separate the hierarchies in the name. Suppose a module named Mod.sub, then it is a module of mod. Therefore, you can assume that the module mod.sub defines all its values in table Mod.sub, which is a table stored in the table mod with the key as a sub. It's like the following definition:

Copy Code code as follows:

Local MoD = {sub = {}}

When require a module mod.sub, require queries the table package.loaded and Package.preload with the original module name "Mod.sub" as the key, where the points in the module name do not have any meaning when searching. However, when searching for a file that defines a child module, require converts the point to another character, usually the system's directory separator, after which require searches for the name as if it were a different name. For example, the path is the following string:
Copy Code code as follows:

?;?. lua;c:\windows\?;/ Usr/local/lua/?/? Lua

Then, when we require "mod.sub", we will try to open the following file:

Copy Code code as follows:

Mod\sub
Mod\sub.lua
C:\windows\mod\sub
/usr/local/lua/mod/mod/sub.lua

With such a load policy, you can organize all the modules in a package into a single directory. Like these small functions, will be combined into a lot of kinky tricks, although in the actual project used not much, but it is fun to play.

Summarize

This article mainly summarizes two very important functions require and module in Lua. Hope to be useful to everyone. For today's development, what all pay attention to module development, and this article summarizes is the module development needs to use two important functions. Everyone in the future to build their own modules, if there is no understanding, where not clear, you can go back to read this article, or can be a direct message and I exchange. I believe that sharing and communication make us more progress.

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.