lua加载函数require和dofile

Lua提供高级的require函数来加载运行库。粗略的说require和dofile完成同样的功能但有两点不同:

1. require会搜索目录加载文件

2. require会判断是否文件已经加载避免重复加载同一文件

由于上述特征,require在Lua中是加载库的更好的函数。

require使用的路径和普通我们看到的路径还有些区别,我们一般见到的路径都是一个目录列表。require的路径是一个模式列表,每一个模式指明一种由虚文件名(require的参数)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号的文件名。匹配的时候Lua会首先将问号用虚文件名替换,然后看是否有这样的文件存在。如果不存在继续用同样的方法用第二个模式匹配。例如,路径如下:

?;?.lua;c:\windows\?;/usr/local/lua/?/?.lua

调用require "lili"时会试着打开这些文件:

lili

lili.lua

c:\windows\lili

/usr/local/lua/lili/lili.lua

require关注的问题只有分号(模式之间的分隔符)和问号,其他的信息(目录分隔符,文件扩展名)在路径中定义。

为了确定路径,Lua首先检查全局变量LUA_PATH是否为一个字符串,如果是则认为这个串就是路径;否则require检查环境变量LUA_PATH的值,如果两个都失败;require使用固定的路径(典型的"?;?.lua")

require的另一个功能是避免重复加载同一个文件两次。Lua保留一张所有已经加载的文件的列表(使用table保存)。如果一个加载的文件在表中存在require简单的返回;表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名require同一个文件两次,将会加载两次该文件。比如require "foo"和require "foo.lua",路径为"?;?.lua"将会加载foo.lua两次。我们也可以通过全局变量_LOADED访问文件名列表,这样我们就可以判断文件是否被加载过;同样我们也可以使用一点小技巧让require加载一个文件两次。比如,require "foo"之后_LOADED["foo"]将不为nil,我们可以将其赋值为nil,require "foo.lua"将会再次加载该文件。

一个路径中的模式也可以不包含问号而只是一个固定的路径,比如:

?;?.lua;/usr/local/default.lua

这种情况下,require没有匹配的时候就会使用这个固定的文件(当然这个固定的路径必须放在模式列表的最后才有意义)。在require运行一个chunk以前,它定义了一个全局变量_REQUIREDNAME用来保存被required的虚文件的文件名。我们可以通过使用这个技巧扩展require的功能。举个极端的例子,我们可以把路径设为"/usr/local/lua/newrequire.lua",这样以后每次调用require都会运行newrequire.lua,这种情况下可以通过使用_REQUIREDNAME的值去实际加载required的文件。

dofile:

我们知道一个lua文件是作为一个代码块(chunk)存在的,其实质就是一个函数,那么最简单的,我在一个外部lua文件中写一段代码,然后在主lua文件中用dofile调用,外部文件的代码块就会执行了。

外部lua文件在编译时并没有涉及词法域:

 --outer.lua:
         function sayHello()
         print(name .. ",你好!")
end
--mian.lua:
 require("outer")
 name = "雷叔"
 local name = "牛叔"
 sayHello() 

运行结果是“雷叔,你好!”

形式上很类似C语言的#include<...>,在其他地方定义的函数,经这么引入文件之后就可以调用了。不过lua并不是定义和实现分离 的语言,这样是把整个定义部分都加载进来了哦。加载过程大致上是lua先加载这个外部文件,然后运行它。实际上这段外部代码是可以有返回值的,它的返回值 就是require的返回值。这里我们什么返回值都没有,执行这个外部代码的结果就是定义了这么个全局函数。注意是全局函数,虽然通常我们直接定义的函数 都是全局函数所以都没怎么注意过,要是非要定义个局部函数在主程序块里可就看不到了。另外和前面的情形一样,外部代码块只认识“雷叔”,“牛叔”是谁它根 本不知道。
        可以在外部文件里定义一堆函数,然后全都加到全局环境下。不过全局的东西用起来要小心,有一个原则是对全局的“污染”越小越好。那么自然就引入了“模块” 的概念。在lua中,模块由万能的table来充当。最自然的想法就是定义一个table,然后把要定义的函数放在这个table里,最后返回这个 table就行了。

lua中的require机制:

为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来。
现在看看lua的require的处理流程。

1、require机制相关的数据和函数
    package.path:保存加载外部模块(lua中"模块"和"文件"这两个概念的分界比较含糊,因为这个值在不同的时刻会扮演不同的角色)的搜索路径,这种路径是"模板式的路径",它里面会包含可替代符号"?",这个符号会被替换,然后lua查找这个文件是否存在,如果存在就会调用其中特定的接口。典型的值为:
    "./?.lua;./?.lc;/usr/local/?/init.lua"
    如果lua代码中调用:require("hello.world")
    那么lua会依次查找:
    ./hello/world.lua ==>这里"hello.world"变成了"hello/world",并替换了模型"./?.lua"
    ./hello/world.lc
    .....
    (这种处理方式和python类似,只不过不需要__init__.py,也有调用python中的__init__.py) package.path在虚拟机启动的时候设置,如果存在环境变量LUA_PATH,那么就用该环境变量作为它的值,并把这个环境变量中的";;"替换为luaconf.h中定义的默认值,如果不存在该变量就直接使用

luaconf.h定义的默认值
    
    package.cpath:作用和packag.path一样,但它是用于加载第三方c库的。它的初始值可以通过环境变量    LUA_CPATH来设置
    
    package.loadlib(libname, func):相当与手工打开c库libname, 并导出函数func返回,loadlib其实是ll_loadlib

2.require的处理流程:
   require(modelname)
   require(在lua中它是ll_require函数)的查找顺序如下:
       a.首先在package.loaded查找modelname,如果该模块已经存在,就直接返回它的值
       b.在package.preload查找modelname, 如果preload存在,那么就把它作为loader,调用loader(L)
       c.根据package.path的模式查找lua库modelname,这个库是通过module函数定义的,对于顶层的lua库,文件名和库名是一样的而且不需要调用显式地在lua文件中调用module函数(在ll_require函数中可以看到处理方式),也就是说lua会根据lua文件直接完成一个loader的初始化过程。
       d.根据package.cpath查找c库,这个库是符合lua的一些规范的(export具有一定特征的函数接口),lua先已动态的方式加载该c库,然后在库中查找并调用相应名字的接口,例如:luaopen_hello_world
       e.以第一个"."为分割,将模块名划分为:(main, sub)的形式,根据package.cpath查找main,如果存在,就加载该库并查询相应的接口:luaopen_main_sub,例如:先查找hello库,并查询luaopen_hello_world接口
       f.得到loder后,用modname作为唯一的参数调用该loader函数。当然参数是通过lua的栈传递的,所以loader的原型必须符合lua的规范:int LUA_FUNC(lua_State *L)
         
       ll_require会将这个loader的返回值赋给package.loaded[modelname],如果loader不返回值同时package.loaded[modelname]不存在时, ll_require就会把package.loaded[modelname]设为true。最后ll_reuqire把package.loaded[modelname]返回给调用者。

3.module的处理流程
    module(name, cb1, cb2, ...)
    
    a.如果package.loaded[name]是一个table,那么就把这个table作为一个mod
    b.如果全局变量name是一个table,就把这个全局变量作为一个mod
    c.创建table:t = {[name]=package.loaded[name], ["_NAME"]=name, ["_M"]=t, ["_PACKAGE"]=*name*(删除了最后的".XXXX"部分)}. 如果name是一个以点分割的串,那么得到的mod类似这个样子:
      hello.world==> {["hello"]={["world"]={XXXXXXX}}}
    d.依次调用cbs:
      cb1(mod), cb2(mod),...
      
    e.将当前模块的环境设置为mod,同时把package.loaded[name] = mod

lua加载函数require和dofile,布布扣,bubuko.com

时间: 2024-10-06 13:13:12

lua加载函数require和dofile的相关文章

php 自动加载函数、自动加载方法、自动加载类

在PHP开发过程中,如果希望从 外部引入一个class,通常会使用include和require方法,去把定义这个class的文件包含进来.这个在小规模开发的时候,没什么大问 题.但在大型的开发项目中,这么做会产生大量的require或者include方法调用,这样不因降低效率,而且使得代码难以维护,况且 require_once的代价很大. 在PHP5之前,各个PHP框架如果要实现类的自动加载,一般都是按照某种约定自己实现一个遍历 目录,自动加载所有符合约定规则的文件的类或函数. 当然,PHP

9)添加自动加载函数

注意:我把之前的控制器文件名都加了C,不然,那个自动记载函数,我不会写 目录雏形: 我开始把自动加载类放在index.php代码的最下面,然后就报了这个错误: 因为我把代码中只要是类加载的地方,全部都注释了,所以,所以他报了--------Controller这个类没加载到,哎 气死我了,然后我发现,那个自动加载函数根本没进去,因为我加了这行代码,根本没有输出出来: 后面,我才发现,原来这个index.php代码在前面的第11行发现了错误,那么后面可定就不运行了啊,哎,真是敲得代码少,这样的问题

php 加载函数 __autoload(), spl_autoload_register()

来自:http://www.cnblogs.com/myluke/archive/2011/06/25/2090119.html spl_autoload_register (PHP 5 >= 5.1.2) spl_autoload_register — 注册__autoload()函数 说明 bool spl_autoload_register ([ callback $autoload_function ] ) 将函数注册到SPL __autoload函数栈中.如果该栈中的函数尚未激活,则激

js页面加载函数

在未加载完文档,使用jquery选择器选择元素后,如果立即绑定事件进行调用,会引起js的报错(can not read property of undefined),导致事件不能绑定成功. alert($("p")[1].firstChild.nodeValue); 这时就需要js的页面加载函数 一般有三种 window.onload = function(){ $("p").bind("click", function() { $(this).

高阶函数 - 惰性加载函数

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>高阶函数 - 惰性加载函数</title> </head> <body> <div style="width: 100px; height: 100px; background: #f60;" id=&quo

js和jquery页面初始化加载函数的方法及先后顺序

原文:js和jquery页面初始化加载函数的方法及先后顺序 运行下面代码.弹出A.B.C.D.E的顺序:A=B=C>D=E. jquery:等待页面加载完数据,以及页面部分元素(不包括图片.视频), js:是页面全部加载完成才执行初始化加载. <!DOCTYPE html> <html> <head> <title>首页</title> <script type="text/javascript" src=&quo

lua加载DLL

.cpp //若没有在项目属性--库文件.依赖文件.包含添加.则添加一下路径 #pragma  comment (lib,"lua5.1.lib") #include "stdafx.h" #include "lua.hpp" extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h"

__autoload() 类文件自动加载函数

这个魔术方法是用来自动加载程序所用到类文件的PHP源文件,这样就避免了我们一个一个自动去require或者include了,这个函数会在试图使用尚未被定义的类时自动调用.通过调用此函数,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类,但是我最近看过一篇文章,上面说__autoload()的效率不是很高.以下是示例代码: 1 <?php 2 功能:在当前页使用类时自动加载该类的文件 3 参数$className 为类的名称 可自定义 4 5 */ 6 7 function __auto

Angular Material串串学客户端开发 2 - Node.js模块加载机制Require()

题外话解一下博客标题,因为第一篇文章评论中,有人质疑离题很远,说了半天和Angular Material没有半毛关系.其实我的的中心在后半句<串串学客户端开发>. require() 不要把这里的Require()和RequireJS混为一谈.不过有意思的是,Typescript的模块定义,甚至同时支持这两种模块机制. 导入和使用外部模块,只是简单的一句require(),看看angular/material/docs下的编译文件gulpfile.js的代码片段.对模块导入和使用有个直观的感觉