2014.6.12
第一章:开始
1.2词法规范
下划线家一个大写字母或者多个大写字母的标志,lua将这类标识符留作特殊用途,哑变量使用。哑变量的百度词条如下:哑变量又被称为虚设变量,用以反映质的属性的一个人工变量,是量化的自变,通常取值为0或者1。
表示未懂,准备留下具体例子。
2.类型与值
2.5table(表)
文中说,lua中table的本质是对象,而对象的意义一般而言内存中具有类型的区域,这是C++中对象的含义。
Lua不会产生table的副本或者创造新的table,table是不能被声明的,他直接由构造表达式生成。
table永远是匿名的,一个持有table的变量鱼table之间没有固定的关联性。也就是更改了table的引用,同时改变了table本身。
区别对待a.x 与 a[x],前者表示a["x"],儿后者以变量x的值访问table。
table将nil作为数组结尾的标志。
table初始化中存在不能使用负数索引,不能使用运算符作为记录的字段名。但是在方括号中可以使用这样的方式初始化。
第五章:函数
5.1多重返回值
return中圆括号与函数返回几个值无关,其他地方强制返回一个值。
5.2变长参数
select()函数返回变长参数的长度,select(“#” ,....)返回所有变长参数的总数,包括nil.
第六章:深入函数
第一类值:表示函数与其他传统类型的值(例如:数字或者字符串)具有同样的权利。
词法域:一个函数嵌套在另一个函数中,内部函数可以访问外部函数的变量。
语法糖:指的是计算机中添加某种语法对语言功能并没有什么帮助,但是方便程序员的使用,通常来说使用语法糖能够增加程序的可读性,从而减少程序出错的机会。
6.1闭合函数
作为第一类值使用的函数,同时能使用词法域,例如:非局部的变量用法。同时非局部变量不会被释放的主要原因可能在于函数被返回,增加了引用。
6.2非全局函数
迭代函数:
local fact = function (n) if n == 0 then return 1 else return n * fact(n - 1) end end fact(5)
报错,因为fact并未定义完全。但是,下面代码:
function fact(n) if n == 0 then return 1 else return n * fact(n - 1) end end fact(5)不会报错。调用了fact 并不是函数本身。但是具体的还是不懂内部原因,个人看来fact函数同样是没有定义完全。
后文提到局部函数的定义 local function foo (<参数>) <函数体>end
等价于
loca foo
foo = function (<参数>)<函数体>end
解释就比较通畅了。
这种技巧对于间接递归无用,必须使用前向声明。
6.3正确的尾调用
类似于goto很奇特的一个东西,不会保存任何该函数的栈信息。也就意味着不会耗费栈空间。
尾调用的判断准则:一个函数在调用完另一个函数后,是否就无所事事需要做了。
第七章:迭代器与泛型for
7.2泛型for的语法
for结构的三个变量迭代函数f,恒定状态s,控制变量a。 a1= f(s,a0)
7.3无状态迭代器
自身不不保存任何状态的迭代器。需要具体例子说明为ipairs函数
local function iter(a,i) i = i + 1 local v = a[i] if v then return i,v end end function ipair(a) return iter,a,0 end test = {1,2,3 } for i,v in ipair(test) do print(i,v) end
这个例子很好的说明for原理,ipaires返回的是iter 作为迭代函数,a作为恒定状态,0作为控制变量初始值。i和v为多值返回值,状态变量为i,第一个为状态变量。
无状态迭代器避免创建新的闭包,没有使用非局部变量upvalue。
7.4复杂状态迭代器
两种解决方案:1.闭包。2.table。通常忽略控制变量。
举例:
local iterator function allword() local state = {line = io.read(),pos = 1} return iterator,state end function iterator(state) while state.line do -- something return state end return nil end
第八章:编译、执行与错误
8.1编译
loadstring总是在全局环境中编译字符串。表达式求值需要添加return构成一条语句。并且将独立的程序块视为一个匿名函数的函数体,并且具有可变长实参。
函数定义是赋值操作,在运行的时候才完成操作。
8.3错误
调用错误可以指定层级,error("",numb),并且在xpcall中可以定义错误处理函数,debug.traceback与debug.debug。
第十三章:元表与元方法
13.4table访问
其中建立私有索引这块个人有点看不懂,自己动手实现了一下,发现里面问题挺多的。
首先是使用表引用表,访问的问题。
key = {"test"} mt = {key} print(mt[1][1]) -->test
无法通过mt[key]访问,等价于 mt = {{"test"}}
key = {"test"} mt = {} mt[key] = "testagain" print(mt[key])这样就不能再通过mt[1]进行访问了,只能通过[key],如果key为私有,外部不能进行访问。直接print(key)结果为地址。
现在也就能明白何为私有索引。相关链接如下。
http://hi.baidu.com/tanglewish/item/83184b10c5e59925f7625c28
第十五章:模块与包
15.1require函数
关于函数require的具体的运行过程,对其原理的理解很重要。
15.2编写模块的基本方法
在实际运用在需要不断的体认,其中三种方法。
第一种:直接定义模块,首先,必须显示的将模块名放在每个函数定义中,其次,在一个函数中调用另一个函数,需要限定调用函数的名称。
第三种:将模块直接赋给package.loaded。
15.3使用环境
使用环境后,调用同一模块是不需要前缀,会从环境中自动获取添加。导出函数与调用一个私有函数没有区别。
使用环境,带来的问题是无法访问前一个环境变量。解决方案如下。
第一种:继承。第二种:声明一个局部变量,此时必须在所有全局变量前添加G。第三种更加正规,将要使用的函数和模块声明为局部变量。
15.4module函数
项目中使用会很多。等价于:
local modname = .... local M = {} _G[modname] = M package.loaded[modname] = M setfenv(1,M)不是很明白 G这段代码。周一找组长。个人猜想,是因为后面没有return后,需要这个放在全局变量中,在其他模块中使用能直接获取modname,而package.loaded列表不能只是能表示这个模块被加载,因为在没有return的情况下,require会返回loaded中的值,具体的细看require函数。
15.5子模块和包
require中目录分隔符值得注意一下。
module加载子模块时会添加显示支持,module(a,b,c)会将环境变量添加进入,包中的子模块没有显式的联系,也就是不会默认加载了。例如:require(a.b)并不会加载a。
第十六章:面对对象编程
16.2继承
实现继承的代码:
setMetatable(o,self) self.__index = self子类的元表是父类,子类中查找不到函数时,index访问父类的self。
16.3多重继承
多重继承的原理需要理解清楚,这对于元表的使用有帮助。