A detailed description of the modules (module) and Packages (package) in Lua

Source: Internet
Author: User

This article mainly introduces the modules in Lua (module) and Packages (package) in detail, this article explains the Require function, write a module, package.loaded, module functions and other content, the need for friends can refer to the following

Objective

Starting with the Lua5.1 version, new support has been added to modules and packages, but modules and packages are defined and used using require and module. The require is used for modules, and module is used to create modules. Simply put, a module is a library that can be loaded by require. Then we get a global variable that represents a table. This table is like a namespace whose content is everything that is exported in the module, such as functions and constants, and a module that conforms to the specification should return the table to require. Now, let's take a concrete look at the two functions of require and module.

Require function

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

First, to load a module, you have to know where the module is. Knowing where the module is, it can be loaded correctly. How does Lua find this mod when we write code like require "mod"? There's a saying here, I'll say it in detail here.

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

Copy CodeThe code is as follows:
?;?. lua;c:\windows\?;/ Usr/local/lua/?/?. Lua

So, when we require "mod", we try to open the following files:

Copy CodeThe code is 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 actual programming, the path to the LUA file require used for searching is stored in the variable Package.path, and print (Package.path) prints the following on my computer:

Copy CodeThe code is 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 the Lua file that matches the module name, LUA will start looking for the C library; The search address is package.cpath, and on my Computer, print (Package.cpath) outputs the following values:

Copy CodeThe code is 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. Both LoadFile and loadlib simply loaded the code and did not run them, and in order to run the code, require called the code with the module name as a parameter. What if the LUA files and the C library are not found? Let's try and require a thing, such as:

Copy CodeThe code is 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, it will be an error. The above is the general work flow of require.

Kinky tricks

As you can see, the above summarizes the use of modules by their names. But sometimes you need to rename a module to avoid name collisions. For example, there are scenarios where it is necessary to load different versions of the same module in the test and get the 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 is no way to edit the name of the luaopen_* function. To this need for renaming, require used a small trick: If a module name contains hyphens, require will use the contents of the hyphen to create the luaopen_* function name. For example, if the name of a module is a-b,require, its open function is called Luaopen_b, not luaopen_a-b. Now, the requirements for testing the different versions presented above can be solved.

Write a module of our own

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

Copy CodeThe code is 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 table of the module

Above is the simplest module. In the process of writing the code, you will find that you must explicitly place the module name in each function definition, and that when a function calls another function in the same module, it must qualify the name of the called function, but we can make a slight modification to define a local variable of table type in the module. Define and invoke the function within the module by this local variable, and assign the local name to the final name of the module, with the following code:

Copy CodeThe code is as follows:
Local M = {}--locally variable
Complex = M--The local variable is eventually assigned to the module name

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 table of the module

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

Copy CodeThe code is as follows:
Local ModuleName = ...

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

Local M = {}--locally variable
_g[modulename] = M--The local variable is eventually assigned to the module name
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 table of the module

Save the above code as Test1.lua. Write a second file with the following code:

Copy CodeThe code is 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 in the same folder, run the Test2.lua file, and print the results as follows:

Copy CodeThe code is as follows:
Test1
1 3

(PS: If you have three points in the code (...) Unfamiliar classmates, please refer to: "Functions in Lua" article) after this modification, we can completely not define the module name in the module, if you need to rename a module, just need to rename the file to define it.

The careful classmate may notice the return statement at the end of the module, such a return statement, when defining the module is very easy to write, how to do? It would be better if all the module-related setup tasks 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, with the following code:

Copy CodeThe code is as follows:
Local ModuleName = ...

Local M = {}--locally variable
_g[modulename] = M--The local variable is eventually assigned to the module name

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

Sample code Download: Click here to download

What is package.loaded?

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

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

Say "Environment".

As you may notice, when I access other functions in the same module, I need to qualify the names, like m in the code above. When I change a local function inside a module from private to public, the corresponding call to the local function needs to be modified, plus the qualified name. What to do? You can't always change the code. How do I get it done? Remember the environment concept in Lua, which is where the environmental concepts come in handy.

We can let the main block of the module have an exclusive environment so that not only all its functions can share the table, but all its global variables are also recorded in the 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 a module name and package.loaded. For example, the following code can be done:

Copy CodeThe code is as follows:
Local ModuleName = ...

Local M = {}--locally variable
_g[modulename] = M--The local variable is eventually assigned to the module name

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

After this, when we write down the following code:

Copy CodeThe code is 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 CodeThe code is as follows:
function M.add (c1, C2)
Return m.new (C1.R + C2.R, c1.i + c2.i)
End

When I call 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, and there are other benefits that you can think about yourself. However, after we call setfenv, we cannot access the global variables in the previous environment after an empty table m as the environment. What's the best way to do that? Several methods are now available.

Method One:

The simplest way to do this is to use a meta-table, set __index, and simulate inheritance in the context of the concept of the environment in Lua. The code is as follows:

Copy CodeThe code is as follows:
Local ModuleName = ...

Local M = {}--locally variable
_g[modulename] = M--The local variable is eventually assigned to the module name

Package.loaded[modulename] = M

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

The above code is very simple, the principle in the previous blog in detail, and here is no longer verbose. Because of the need to set the meta-table, all will have some overhead, but can be ignored.

Method Two:

Copy CodeThe code is as follows:
Local ModuleName = ...

Local M = {}--locally variable
_g[modulename] = M--The local variable is eventually assigned to the module name

Package.loaded[modulename] = M

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

This saves a global environment variable in its own module, and when we access the variables in the previous environment, we need to add the prefix _g, which seems a little cumbersome. However, since there is no meta-method involved, this approach is slightly faster than the method.

Method Three:

This method is the most formal method, that is, the need to use the function or module declared as a local variable, see the following code:

Copy CodeThe code is as follows:
Local ModuleName = ...

Local M = {}--locally variable
_g[modulename] = M--The local variable is eventually assigned to the module name

Package.loaded[modulename] = M

Local sqrt = math.sqrt--We need to use the MATH.SQRT function in our own module, so we'll save it first.
Local IO = io-need to use IO Library and save it
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. You can do it by yourself.

module function

As you may have noticed, when defining a module, the previous lines of code are the same, and are divided into the following steps:

1. Obtain the module name from the parameters passed into the Require;
2. Create an empty table;
3. Add the field of the module name to the global environment _g, and assign the empty table to the field;
4. Set the module in the already loaded table;
5. Set environment variables.

It is these steps that need to be added before the definition of each module, is not a bit of a hassle, a new function module has been provided in Lua5.1, which includes the functions completed by the above. When writing a module, you can replace the preceding setup code directly with the following code:

Copy CodeThe code is as follows:
Module (...)


With this little code above, it creates a new table, assigns it to the global field of the module name and loaded table, and finally sets the table as the main block of the environment. 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 resolved when using the module:

Copy CodeThe code is as follows:
Module (..., package.seeall)


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

Sub-modules and packages

LUA supports hierarchical module names that can be separated by a single point in the name hierarchy. If a module is named Mod.sub, then it is a submodule of the MoD. Therefore, you can assume that the module mod.sub will define all its values in table Mod.sub, which is a table stored in the table mod with the key sub. Is like the following definition:

Copy CodeThe code is as follows:
Local MoD = {sub = {}}


When require a module mod.sub, require uses the original module name "Mod.sub" as key to query table package.loaded and Package.preload, where the points in the module name do not make any sense when searching. However, when searching for a file that defines a submodule, require converts the point to another character, usually the system's directory delimiter, and after the conversion, require searches for the name as if it were a different name. For example, the path is the following string:

Copy CodeThe code is as follows:
?;?. lua;c:\windows\?;/ Usr/local/lua/?/?. Lua

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

Copy CodeThe code is 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 the actual project will not be a lot of use, but it is very interesting 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, everything is about module development, and this article summarizes the two important functions that need to be used for module development. 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 you can directly message and I communicate. I believe that sharing and communication make us more progress.

A detailed description of the modules (module) and Packages (package) in Lua

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.