lua里的一些特殊方法和变量

1.pcall (f, arg1, ···):pcall在保护模式(protected mode)下执行函数内容,同时捕获所有的异常和错误。若一切正常,pcall返回true以及“被执行函数”的返回值;否则返回false和错误信息(打印出来即可)。Lua 代码可以显式的调用 error 函数来产生一条错误。

Calls function f with the given arguments in protected mode. This means that any error inside f is not propagated; instead, pcall catches the error and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In such case, pcall also returns all results from the call, after this first result. In case of any error, pcall returns false plus the error message.

2.xpcall 接受两个参数:调用函数、错误处理函数。xpcall (f, err)

his function is similar to pcall, except that you can set a new error handler.

xpcall calls function f in protected mode, using err as the error handler. Any error inside f is not propagated; instead, xpcall catches the error, calls the err function with the original error object, and returns a status code. Its first result is the status code (a boolean), which is true if the call succeeds without errors. In this case, xpcall also returns all results from the call, after this first result. In case of any error, xpcall returns false plus the result from err.

当错误发生时,Lua会在栈释放以前调用错误处理函数,因此可以使用debug库收集错误相关信息。常用的debug处理函数:debug.debug()和debug.traceback(),前者给出Lua的提示符,你可以自己动手察看错误发生时的情况;后者通过traceback创建更多的错误信息,也是控制台解释器用来构建错误信息的函数。你可以在任何时候调用debug.traceback获取当前运行的traceback信息。
3._G 存储全局变量的table

在Lua中,要声明全局变量很简单,那就是定义变量的时候,前面不要加上local。这个神秘的全局变量,其实本质上也是一个table,它把我们创建的全局变量都保存到一个table里了。而这个table的名字是:_G

A global variable (not a function) that holds the global environment (that is, _G._G = _G). Lua itself does not use this variable; changing its value does not affect any environment, nor vice-versa. (Use setfenv to change environments.)

 -- 定义一个全局变量

 number = "1";

 -- 用三种方式输出变量的值

 print(number );

print(_G["number "]);

print(_G.number );

输出结果如下:

1
1
1

4.改变函数的全局变量环境——setfenv函数

setfenv函数就是用来改变某个函数范围里的全局环境的,通俗地说,就是把某个函数范围内的_G给弄没了。

setfenv函数两个参数分别代表:

1). 第一个参数,可以是即将要改变环境的函数,也可以是一个数字。数字1代表当前函数,数字2代表调用当前函数的函数,后面以此类推。

2).第二个参数,新的全局环境table

  -- 定义一个全局变量

  number= "123";

 -- 将当前全局环境重新设置为新的table

 setfenv(1, {});

  -- 输出值

 print(number);

如果现在运行代码,输出结果将会是这样的:

attempt to call global ‘print’ (a nil value)

为什么?很出乎意料的脸print函数都无法找到了?

这是因为我们已经把当前函数范围内的全局变量环境改变了,全局变量默认是保存在_G中的,而现在的全局变量是在一个新的table里。目前这个table是空的,所以不存在任何全局变量。

setfenv函数就是用来改变某个函数范围里的全局环境的,通俗地说,就是把某个函数范围内的_G给弄没了。

setfenv函数两个参数分别代表:

1). 第一个参数,可以是即将要改变环境的函数,也可以是一个数字。数字1代表当前函数,数字2代表调用当前函数的函数,后面以此类推。

2).第二个参数,新的全局环境table。

5.保留原来的_G

现在连print函数都无法使用了,对于测试很不方便,我们可以做个小动作,把原来的_G保留起来。

如下代码:

 -- 定义一个全局变量

 number= "123";   

-- 将当前全局环境重新设置为新的table

 setfenv(1, {g = _G})

-- 输出值

 g.print(number);

   -- 再次定义一个全局变量

  gName = "456";

   -- 再次输出值

 g.print(number);

  -- 输出原来的值

  g.print(g.number);

只要在定义新的环境时,把_G作为一个字段放到新的table里,就可以调用原来的全局变量了。

那么,输出结果如下:

nil
456
123

三次调用g.print函数的输出结果都是不一样的:

a.第一次,此时刚刚重新设置了全局环境,这时候当前函数的全局变量只有一个,那就是g,所以number的值是nil。

b.第二次,我们再一次对number进行赋值,此时,已经在新的环境中了,所以接下来输出的number值是存在的。

c.第三次,这次输出的是g.number的值,通过g调用的number值是原先的全局环境里的值,所以number的值仍然是最初的“123”。

6.最后使用__index元方法保留原来的_G

这里还有一个小技巧分享一下,刚刚举例保留_G,但是调用print等函数时还需要形如g.print的方式,有点碍事。

我们可以利用__index来解决这个问题,如下代码:

  -- 定义一个全局变量

 number = "123";

 -- 一个table,即将成为新的环境 

 local newG = {};

  setmetatable(newG, {__index = _G});

 -- 将当前全局环境重新设置为新的table

  setfenv(1, newG);   

  number = "456!"; 

 -- 输出值

  print(number );

  print(_G.number );

我们给新的table设置一个元表,这个元表的__index元方法就是_G。

于是,当新的环境里找不到print字段时,就会去_G里寻找。

输出结果如下

456
123

第一次输出的是新环境里的number 值,第二次输出的是原来环境里的number 值,互不影响。

7.lua的require函数有两个特性:1.require函数会搜索目录加载文件.2.require会判断是否文件已经加载避免重复加载同一文件。所以,这个函数只能加载一次文件,当我们加载的lua文件动态改变后,需要重复加载时,只需要在记载前调用下package.loaded[“文件名.lua”] = nil就ok了,然后再required(‘文件名.lua’)。

8.package.path和 package.cpath

package.path用于搜索自己写的库文件或者第三方的库文件。package.path    = "./script/?.lua;" .. package.path

package.cpath用于搜索自己写的so库文件或者第三方的so库文件或者dll文件。 package.cpath    = "./?.dll;./script/?.dll;" .. package.cpath

对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。

require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。

当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以)。

当我们调用 require("module") 时就会尝试打开文件目录去搜索目标。如果找到目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。

用法:

1 只加载想要的目录
package.path = "../myLuaTest/myLuaCode/?.lua;" 

2 增加目录
package.path = "../myLuaTest/myLuaCode/?.lua;"..package.path  在原有的目录上加上自己的。 

还有一种方法(并没有试过)

例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里:

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

文件路径以 ";" 号分隔,最后的 2 个 ";;" 表示新加的路径后面加上原来的默认路径。

接着,更新环境变量参数,使之立即生效。

source ~/.profile

 8.module模块:module用来来定义模块

module(...) 操作就相当于一下操作。

local modname = ... 

local M = {}  

_G[modname] = M  

package.loaded[modname] = M     这步是为了消除末尾的return语句,将模块table直接赋值给package.loaded

--[[       和普通Lua程序块一样声明外部函数。       --]]  

setfenv(1,M)  

有一点容易被忽略掉,module 指令运行完后,整个环境被压栈,所以前面全局的东西再看不见了。比如定义了一个 test 模块,使用

module("test")

后,下面不再看的见前面的全局环境。如果在这个模块里想调用 print 输出调试信息怎么办呢?一个简单的方法是

local print=print
module("test")

这样 print 是一个 local 变量,下面也是可见的。或者可以用

local _G=_G
module("test")

那么 _G.print 也是可以用的。

注意:关于module( ... , package.seeall)。 一般在一个Lua文件内以module函数开始定义一个包。module同时定义了一个新的包的函数环境,以使在此包中定义的全局变量都在这个环境中,而非使用包的函数的环境中。理解这一点非常关键。 “module(..., package.seeall)”的意思是定义一个包,包的名字与定义包的文件的名字相同,并且在包的函数环境里可以访问使用包的函数环境。使用方式:一般用require函数来导入一个包,要导入的包必须被置于包路径(packagepath)上。包路径可以通过package.path或者环境变量来设定。一般来说,当前工作路径总是在包路径中。

再调用module函数时,多传入一个package.seeall的参数,相当于 setmetatable(M, {__index = _G}) 这样全局的_G就不会消失。

9. collectgarbage:用来控制自动内存管理,当设置了setstepmul和setpause,Lua便会开启自动垃圾回收。

Lua 实现了一个增量标记-扫描收集器。 它使用这两个数字来控制垃圾收集循环: 垃圾收集器间歇率和垃圾收集器步进倍率。 这两个数字都使用百分数为单位 (例如:值 100 在内部表示 1 )。

垃圾收集器间歇率控制着收集器需要在开启新的循环前要等待多久。 增大这个值会减少收集器的积极性。 当这个值比 100 小的时候,收集器在开启新的循环前不会有等待。 设置这个值为 200 就会让收集器等到总内存使用量达到 之前的两倍时才开始新的循环。

垃圾收集器步进倍率控制着收集器运作速度相对于内存分配速度的倍率。 增大这个值不仅会让收集器更加积极,还会增加每个增量步骤的长度。 不要把这个值设得小于 100 , 那样的话收集器就工作的太慢了以至于永远都干不完一个循环。 默认值是 200 ,这表示收集器以内存分配的"两倍"速工作。

如果你把步进倍率设为一个非常大的数字 (比你的程序可能用到的字节数还大 10% ), 收集器的行为就像一个 stop-the-world 收集器。 接着你若把间歇率设为 200 , 收集器的行为就和过去的 Lua 版本一样了: 每次 Lua 使用的内存翻倍时,就做一次完整的收集。

  • collectgarbage("collect"): 做一次完整的垃圾收集循环。通过参数 opt 它提供了一组不同的功能:

  • collectgarbage("count"): 以 K 字节数为单位返回 Lua 使用的总内存数。 这个值有小数部分,所以只需要乘上 1024 就能得到 Lua 使用的准确字节数(除非溢出)。

  • collectgarbage("restart"): 重启垃圾收集器的自动运行。

  • collectgarbage("setpause"): 将 arg 设为收集器的 间歇率 。 返回 间歇率 的前一个值。

  • collectgarbage("setstepmul"): 返回 步进倍率 的前一个值。

  • collectgarbage("step"): 单步运行垃圾收集器。 步长"大小"由 arg 控制。 传入 0 时,收集器步进(不可分割的)一步。 传入非 0 值, 收集器收集相当于 Lua 分配这些多(K 字节)内存的工作。 如果收集器结束一个循环将返回 true 。

  • collectgarbage("stop"): 停止垃圾收集器的运行。 在调用重启前,收集器只会因显式的调用运行。

比如下面:

collectgarbage("collect");
collectgarbage("setpause", 100);
collectgarbage("setstepmul", 500);

https://blog.csdn.net/ChinarCSDN/article/details/78667262  sublime3和lua的安装

10.math.random和math.randomseed

1.math.randomseed接收一个整数 n 作为随机序列种子。对于相同的随机种子, 生成的随即序列一定是相同的。所以程序每次运行, 赋予不同的种子很重要。很自然想到使用系统时间作为随机种子,可以看到前两次运行的随机数都是一样的。究其原因,就是 os.time() 返回的时间是秒级的, 不够精确, 而 random() 还有个毛病就是如果 seed 很小或者seed 变化很小,产生的随机序列仍然很相似,math.randomseed(tostring(os.time()):reverse():sub(1, 6)),就是把 time返回的数值字串倒过来(低位变高位), 再取高位6位。 这样, 即使 time变化很小, 但是因为低位变了高位, 种子数值变化却很大,就可以使伪随机序列生成的更好一些。https://blog.csdn.net/zhangxaochen/article/details/8095007 
这里需要注意的是,如果随机时给的随机范围不同,序列时不一样的。并且在给定了随机种子的前提下,我们即使不给范围随机48次,当第49和50次都给随机范围随机的结果跟我们50次都给范围随机的结果时一样的。

 math.random([n [, m]]) 有三种用法: 无参调用, 产生 [0,1) 之间的浮点随机数; 只有参数 n, 产生 [1,n] 之间的整数; 有两个参数 [n,m], 产生 [n,m]之间的随机整数.

种子数只是随机算法的起源数字,和生成的随机数的区间没有任何关系。

猜你喜欢

转载自blog.csdn.net/weixin_39407066/article/details/83783905