
在Lua 52及之前的版本,所有数值都以双精度浮点格式表示,从53版本开始,Lua语言的number分为integer的64位整型和被称为float的双精度浮点型,若想要32位的整型和单精度浮点类型,可以将Lua 53编译为精简Lua(Small Lua)模式
可以使用%a参数进行格式化输出,如:
这种格式很难阅读,但是可以保留所有浮点数的精度,并且比十进制转换速度更快
// 区分整数和浮点数,由于53版本之前没有整数的概念,所以mathfloor永远返回整数,// 的 *** 作数只要有一个浮点数,则向下取整返回浮点数
取模运算的定义:
由此可知,取模和取余数差不多,当想要一个值不超过某个范围的时候,就对该值进行取模, 在整数的情况下, a % b 取模的值永远在[1,b-1]范围内,即 5 % 3的取模结果在[1,3]之间
Lua语言的关系运算六种
标准数学库math,包括三角函数(sin,cos,tan,asin等)、指数函数、取整函数、最大和最小函数max和min,random以及常量pi和huge(最大可表示数值,在大多数平台上代表inf)
mathrandom
mathrandomseed 用于设置伪随机数发生器的种子,看例子就明白了了
mathrandomseed每次设置一样的值,则mathrandom就会生成一样的随机数,不管mathrandom带不带参数。
三个取整函数:
floor 向负无穷取整;
ceil 向正无穷取整;
modf 向0取整,并会返回小数部分作为第二个结果;
如果想将数值x向最近的整数取整,这个问题没看懂??????
数学库中的常量定义了整型值的最大值mathmaxinteger和最小值mathmininteger
这段内容也没完全看懂?????
整型转行成浮点型
浮点型转行成整型
可以充分利用mathtointeger函数来判断一个数值是否为整数
从上到下,优先级从高到低
在二元运算符中,除了幂运算和连接 *** 作符是右结合外,其他都是左结合
Lua 53 支持的最大整数为2^63,而Lua 52支持的最大整数为2^53,由于52及以下版本没有整数,所以跟整数相关的函数都不支持,如mathtype()、mathmaxinteger、mathmininteger、mathtointeger等
调用 GetLuaVar(luastate,"ProjectAone") 把ProjectAone的值取到堆栈上,然后根据类型自己用 lua_tonumber(luastate, -1) 或 lua_tostring(luastate, -1)得数据void GetLuaVar(lua_State pLuaState,const char strVarName){const char pIndS = strVarName;const char pIndE = strVarName;T_B8 bParts = false;//判断是否分两部分,因为第一部分要用lua_getglobal,其他部分用lua_gettablewhile( 0 != pIndE ){if( '' == pIndE ){if(bParts){lua_pushstring(pLuaState,std::string(pIndS,pIndE - pIndS)c_str());lua_gettable(pLuaState,-2);lua_remove(pLuaState,-2);}else{lua_getglobal(pLuaState,std::string(pIndS,pIndE - pIndS)c_str());bParts = true;}++pIndE;pIndS = pIndE;}else{++pIndE;}}if(bParts){lua_pushstring(pLuaState,std::string(pIndS,pIndE - pIndS)c_str());lua_gettable(pLuaState,-2);lua_remove(pLuaState,-2);}else{lua_getglobal(pLuaState,std::string(pIndS,pIndE - pIndS)c_str());}}
很多人都知道lua中_index用于查询,_newindex用于更新,但是应用起来还是很模糊,我在这针对这做了个详细的讲解。
在 Lua table 中我们可以访问对应的key来得到value值,但是却无法对两个 table 进行 *** 作。
因此 Lua 提供了元表(Metatable),允许我们改变table的行为,每个行为关联了对应的元方法。
例如,使用元表我们可以定义Lua如何计算两个table的相加 *** 作a+b。
当Lua试图对两个表进行相加时,先检查两者之一是否有元表,之后检查是否有一个叫"__add"的字段,若找到,则调用对应的值。"__add"等即时字段,其对应的值(往往是一个函数或是table)就是"元方法"。
有两个很重要的函数来处理元表:
setmetatable(table,metatable):对指定table设置元表(metatable),如果元表(metatable)中存在__metatable键值,setmetatable会失败 。
getmetatable(table):返回对象的元表(metatable)。
__index 元方法
这是 metatable 最常用的键。
当你通过键来访问 table 的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的__index 键。如果__index包含一个表,Lua会在表格中查找相应的键。
Lua查找一个表元素时的规则,其实就是如下3个步骤:
1在表中查找,如果找到,返回该元素,找不到则继续
2判断该表是否有元表,如果没有元表,返回nil,有元表则继续。
3判断元表有没有__index方法,如果__index方法为nil,则返回nil;如果__index方法是一个表,则重复1、2、3;如果__index方法是一个函数,则返回该函数的返回值。
__newindex 元方法
__newindex 元方法用来对表更新,__index则用来对表访问 。
当你给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数而不进行赋值 *** 作。
__call 元方法
__call 元方法在 Lua 调用一个值时调用。
__tostring 元方法
__tostring 元方法用于修改表的输出行为。
下面我们来看些具体的例子:
tableA中查找到了属性,直接赋值
这里把tableB设置成tableA的元表,tableA赋值run()方法和age属性,我们知道tableA中本来是不存在run()的,去查找tableA的__index属性,发现没有,这时候就会去tableA的元表中查询,tableA有元表且元表中存在属性或方法,则直接赋值。
我们接着例1来看,我们发现tableA有元表tableB,且tableB中不存在属性,去元表查找__index,__index是个本身是个表,也可以赋值,打印发现赋值成功。
当tableA中存在有__index,且__index是个表,表中已有赋值属性,可以赋值。
这时候不管tableA有没有元表,且自身或元表内是否有__newindex,都不影响赋值。
赋值 *** 作有打印,因为tableA有元表tableB,且元表tableB有__newindex,检测到元表中有__newindex,__newindex又是个function的话,直接调用方法。
tableArun()和tableAage报错,是因为tableA本身没有这两个属性,赋值的时候因为__newindex是function,所以赋值不成功,元表tableB中又没有__index,所以是找不到属性的,调用就会报错。
注意:虽然tableB作为tableA的元表,有age属性,但是没有__index,是取不到的。仔细琢磨下
这例子中我们在tableB改了__index = people,这时候我们调用的时候,能够在元表中找到,但赋值的时候因为__newindex是function,其实没有赋值成功,所以属性值还是之前的。
__newindex是个表,这个时候赋值,是给other这个表赋值,跟tableA没关系了
nginx报错:nginx: [emerg] unknown directive in /etc/nginx/confd/testconf:4
解决: 第四行出现了 tab 空格 , 换成正常的空格即可
参考:
nginx报错:nginx: [emerg] unknown directive in /etc/nginx/confd/testconf:4
思考一下:如果现在定义了一个table a,将table a赋值给table b,此时它们的内存情况是什么样呢?
ab都会指向同一个内存块,如果a设置为nil,b依旧能访问该内存块的元素,直到b设置为nil后,Lua的垃圾回收机制会清理相应的内存。所以当b在更改table内的值后,a再去访问的时候,值也是改变的。
table的for循环写法
先解释一下上面的各个变量
在table中,我们无法对两个table之间进行 *** 作,比如:table a+ table b。为了解决这个问题,就引入了元表的概念,它允许我们 改变table的行为 。
当我们在进行表a+b的时候:
其实逼逼了这么多,我觉得元表和元方法其实就相当于 重载 ,比如_add,我们就是重载了+ *** 作符
也可以将它理解成 事件驱动 ,元表中的键对应着不同的事件名,关联的值是元方法,元方法里就是我们事件对应的 *** 作。
继续接上面的分析
每个Node都是一个键值对 ,里面包含了key和value。tvk是key的值,但是当我们出现hash冲突,此时lua的hash算法比较特殊,一般情况下,我们的hash算法都是根据key算出hash,然后如果有冲突的话,就放在改位置的链表上。而lua不同, 当它出现hash冲突的时候,会在hash表中找到一个空的位置x,来存放key,并且让冲突处的节点的nknext指向x 。
这意味着什么呢?发生冲突我们无需重新分配空间来存储冲突的key,而是利用hash表上未用过的空白处来存储。刚才我们将key放在了空位置x, 如果此时存在另一个key2,它算出的hash值是空位置x,而我们刚才的key只是因为hash冲突了,占用了其他key的位置,这个时候我们就讲key2放在x上,将key再放到另一个空白处 。
忍不住想总结一波table的实现,和上面我们的Node类型进行对照
lua的table其实由 数组段 和 hash 两部分组成,当你的key值不会过于离散的时候,lua就会将它存储在数组段(也就是下图的array),反正会存储在hash段(也就是下图的node),这个分割线是以数组段的利用率不低于50%为准。hash段采用闭散列的算法,它将有冲突的key存储在空闲槽中,而不额外分配内存。
在我们table结构体中,array和sizearray都是表示数组段。
而lsizenode和node,lastfree是表示hash段。node指向hash表的起始位置,lsizenode是log2(node指向的哈希表的节点数目), lastfree指向node里面最后一个未使用的节点 (因为我们在hash冲突的时候,是从后往前查找未使用的节点,lastfree存储最后一个未使用节点就可以方便查找)
如果此时hash表中已经没有空格了,那么lua就会resize这个hash表(等会再谈lua的动态扩增)
lua创建新表的时候 先为新表分配内存 Table t = luaM_new(L, Table),然后将表 连接到gc 上并设置标志位luaC_link(L, obj2gco(t), LUA_TTABLE),然后 初始化 一些必要的属性,使用setarrayvector为数组段分配内存,setnodevector为hash部分分配内存,最后返回表指针。
table的查找会根据key进行判断,如果key为空就直接返回空,key为字符串就调用luaH_getstr(t, rawtsvalue(key)),key为数字则根据它是否整数调用luaH_getnum(t, k),否则,计算出key的位置,遍历table的node节点,找到对应键所在的节点返回。
因为key为数字比较特殊,所以研究一把luaH_getnum函数的实现
如果key大于等于1,小于数组的长度,则从数组中取出对应的键值,否则利用hashnum找到key对应的node位置,遍历node链表,返回对应的值
dummynode是带头结点的指针。
往table中插入新值,先检测 key的主位置 (main position)是否为空,主位置就是key的哈希值在node中的位置。
如果主位置为空,就直接插入,主位置不为空,检查占领该位置的key的主位置是不是在这个地方,如果不在,则将该key移动到其他空闲位置,将要插入的key插入到这个位置中。如果在这个地方,则将要插入的key插入到一个 空槽 中。
如果找不到空闲位置放新键值,就 rehash函数 ,扩增hash表的大小,再找出新位置,再调用luaH_set把要插入的key插入到新的哈希表中,直接返回LuaH_set的结果。
首先需要确定开发语言,比如c,c++,java,python,php,net等,当开发语言确定后就可以根据语法规则通过代码编辑器开始编写代码了。
软件开发学习是一个非常不错的选择,就目前软件开发而言java专业仍是比较吃香的一个专业,就业也是很不错的。
这专业会学习以下语言:C,JAVA,PHP,PYTHON,MYSQL,JAVASCRIPT另外就是各个语言的框架,提高开发速度的。下面是跟数据相关的知识。
1如果没有语法错误, 则以函数形式返回编译好的代码块; 否则,返回 nil 加上错误消息。
如果结果函数有上值, env 被设为第一个上值。 若不提供此参数,将全局环境替代它。 所有其它上值初始化为 nil。 (当你加载主代码块时候,结果函数一定有且仅有一个上值 _ENV ))。 然而,如果你加载一个用函数(参见 stringdump, 结果函数可以有任意数量的上值) 创建出来的二进制代码块时,所有的上值都是新创建出来的。 也就是说它们不会和别的任何函数共享。
2接下来就是根据以上信息进行下面的 *** 作,chunkname 在错误消息和调试消息中,用于代码块的名字。 如果不提供此参数,它默认为字符串chunk 。 chunk 不是字符串时,则为 "=(load)" 。
字符串 mode 用于控制代码块是文本还是二进制(即预编译代码块)。 它可以是字符串 "b" (只能是二进制代码块), "t" (只能是文本代码块), 或 "bt" (可以是二进制也可以是文本)。 默认值为 "bt"。
3Lua 不会对二进制代码块做健壮性检查。 恶意构造一个二进制块有可能把解释器弄崩溃。
运行程序来遍历表中的所有域。 第一个参数是要遍历的表,第二个参数是表中的某个键。 next 返回该键的下一个键及其关联的值。 如果用 nil 作为第二个参数调用 next 将返回初始键及其关联值。 当以最后一个键去调用,或是以 nil 调用一张空表时, next 返回 nil。 如果不提供第二个参数,将认为它就是 nil。 特别指出,你可以用 next(t) 来判断一张表是否是空的。
索引在遍历过程中的次序无定义, 即使是数字索引也是这样。 (如果想按数字次序遍历表,可以使用数字形式的 for 。)
4当在遍历过程中你给表中并不存在的域赋值, next 的行为是未定义的。 然而你可以去修改那些已存在的域。 特别指出,你可以清除一些已存在的域。
如果 t 有元方法 __pairs, 以 t 为参数调用它,并返回其返回的前三个值。
否则,返回三个值:next 函数, 表 t,以及 nil。
5传入参数,以 保护模式 调用函数 f 。 这意味着 f 中的任何错误不会抛出; 取而代之的是,pcall 会将错误捕获到,并返回一个状态码。 第一个返回值是状态码(一个布尔量), 当没有错误时,其为真。 此时,pcall 同样会在状态码后返回所有调用的结果。 在有错误时,pcall 返回 false 加错误消息。
希望能帮到你,谢谢!
以上就是关于《Lua程序设计》之 数值全部的内容,包括:《Lua程序设计》之 数值、如何解析Lua中的table数据、lua中__index和__newindex的使用等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)