lua 学习笔记 一

1.1
一个程序块就是一连串的语句或命令
几条连续的lua语句之间不需要分隔符,但用分号来分隔语句也是合法的。
退出lua解释器交互模式使用os。exit();
使用-i参数启动lua解释器,解释器会在运行完指定程序后进入交互模式


使用dofile运行程序块
1,加载lua程序
2,调用lua函数


dofile("example.lua") --加载程序库
val = function()  --调用程序库的函数


1.2
Lua中的标识符可以由任意字母,数字和下划线构成,但不能以数字开头。
lua的字母依赖于区域设置,但尽量不要使用区域的特定字母,避免在不支持特定字母的区域出错。
lua中的保留字
and break do else elseif --注意之间是没有空格的,和C/C++不同
in local nil not or
repeat return then true until
while


Lua区分大小写,“And”不是保留字


lua的注释方式:
-- 用于注释一行
--[[ chunk ]]--  用于注释一段代码


1.3
全局变量不需要声明,只需要将值赋予全局变量即可创建。
访问未初始化的变量不会引发错误,将返回一个特殊值nil。
删除某个全局变量,只需要用nil对其赋值。


1.4
可以通过对“_PROMPT”全局变量赋值来修改交互模式的命令提示符
解释器会为所有命令行参数创建一个名为“arg”的table,且脚本名称位于索引0上。


2.0 - 2.4
Lua是一种动态类型语言,值包含了自身的类型信息。
lua中8中基础类型 nil(空) boolean number string userdata(自定义类型) function thread table
函数type可以返回值的类型名称,返回结果为字符型。
变量没有预定义类型,任何变量可以包含任何类型的值
lua中,函数作为“第一类值”(first-class value)来看待,可以像操作其他值一样来操作一个函数值。


Lua中字符采用8位编码,lua字符串中的字符可以具有任何数值编码。字符串是不可变的值(immutable values)。对字符串的修改需要
创建一个新串接收修改后的值。 字符串需要一对匹配的单引号或双引号来界定。
在字符串前放置操作符“#”可以获得字符串的长度,example:a = ‘123’ print(#a) -- 3


lua将false 和nil视为“假”,其他值都视为“真”


2.5-2.7
table的创建通过“构造表达式”来完成,最简单的构造表达式{} 。
table永远是“匿名的”(anonymous),一个持有table的变量和table自身之间没有固定的关联性。
table可以用不同类型的索引来访问value,且在需要容纳新条目时,能够自动增长。
注意:a[x]和a["x"]是不同的,前者以变量x的值来索引,而后者以字符串“x”来索引。
lua中可以用任何数字作为数组索引的起始值,但对于数组,通常以1作为索引的初始值。不少库遵循这个惯例。
Lua将nil作为界定数组结尾的标志。
若数组中有nil值,使用“#”操作符的结果不可预期,可以使用table.maxn来获取数组的最大正索引数。


userdata用于表示一种由应用程序或c语言库所创建的新类型,没有太多的预定义操作,只能进行赋值和相等性测试。


3.0 - 3.4
常规算术操作符:
二元:+ - * / ^(指数) % 
一元  -(负号)
所有操作符都可用于实数


关系操作符:
< > <= >= == ~=(不等于)
所有操作符的运算结果是true 或 false。
== ~=用于相等性和不相等性测试,nil只于其自身相等。对table,userdata和函数,只做引用比较。
“2”<"15"为true (按照字母次序来比较)


逻辑操作符:
and   or    not
and 若第一操作数为假,则返回第一操作数,否则返回第二操作数,or若第一操作数为真,返回第一操作数,否则返回第二操作数。
and 和 or 都使用了短路求值(short-cut evaluation)
(a and b)or c 类似于c语言中得a?b:c 其前提为b不能为假。


字符串连接操作符
“ ..”     lua中字符串是不可变值。


3.6
table 构造式用于创建和初始化table表达式。
列表风格 days = {“Sunday”,...,“Saturday”}   记录风格 a = { x = 10,y = 20}
创建table之后,可以在table中创建或删除其中的某些字段。
Lua中很少用到链表,列表数据一般通过数组来实现。
lua允许在方括号之间,显式地用一个表达式来初始化索引值。 example i = 20; a = { [i+0] = 1,[i+1] = 2}
以0作为一个数组的起始索引 days = {[0]=“Sunday”,...,“Saturday”}


4.0 - 4.4
lua 允许多重赋值 example a,b = 10,20 -- a为10 ,b为20 在多重赋值中,Lua先对等号右边的所有变量求值,然后执行赋值,因此交换两个变量可以写为:
x,y = y,x


多重赋值中,若值的个数少于变量的个数,则多余的变量赋值为nil,若值的个数多,则多得值被丢弃。


通过loacl语句创建局部变量 局部变量的作用于仅限于声明它的那个块。一个块是一个控制结构的执行体,或者一个函数的执行体,或者一个程序块(chunk);
ps:交互模式中每行输入内容自身就形成一个程序块,导致局部变量的使用出现非预期情况,可以通过do - end 来解决


使用局部变量的优点: 一,可以减少命名冲突 ,二,访问速度提高 三,作用域范围有限,对内存的消耗减少。


if then else   while repeat-until  数字型for(numeric for)泛型for(generic for)
if then elseif then else


lua中,一个声明在循环体中得局部变量的作用域包括条件测试:example:
repeat
   local num = 0
   num = num + 1
   until num < 10  --在此处可以访问num


for var = exp1,exp2,exp3 do
--执行代码
end
var从exp1 变换到exp2, 每次变换都以exp3作为步长递增var,并执行一次执行代码,exp3 默认为1.若不想为循环设置上限,则将exp2 设为常量math.huge。


泛型for通过一个迭代器(iterator)函数来遍历所有值。常用迭代器有迭代文件 io.lines,迭代table元素 pairs, 迭代数组元素 ipairs, 迭代字符串单词 
string.gmatch 等,也可以自己编写迭代器。
对for循环, 1,循环变量是循环体的局部变量, 2,决不对循环变量作任何赋值。


break 或return只能是一个块的最后一条语句


5.0 -5.3
函数的所有参数必须放到一对圆括号中,但如果一个函数只有一个参数,且参数是一个字面字符串或者table构造式,则圆括号可有可无。
冒号操作符 : 冒号操作符使调用函数时将调用者作为函数的隐含第一参数,example o.foo(o,x) 也可写成 o:foo(x).
函数的形参和实参的个数匹配会自动调整。以匹配参数表的要求


多返回值函数
lua会调整一个函数的返回值数量以适用不同的调用情况,作为单独语句时,舍弃所有返回值,作为表达式一部分时,返回第一个返回值,当函数是一系列表达式中的最后一个
时(或仅有一个元素),返回全部值。
一系列表达式表现为:多重赋值, 函数实参列表,table构造式,return语句。
lua中函数可以接受不同数量的实参,即变长参数函数。参数列表中的“ ... ”表示函数可以接受不同数量的实参。读取参数时, ... 表示一个由所有变长参数构成的数组。example format函数


6.0 - 6.3
具名实参,实参通过它在参数表中得位置于形参匹配起来,适用table作为函数的参数来实现具名。
函数与其他值一样是匿名的,讨论一个函数名,实际是讨论一个持有某函数的变量。


closure(闭合函数) 一个函数写在另一个函数之内,从而使内部的函数可以访问外部函数中的局部变量。
外部函数的局部变量,对内部函数来说,既不是全局变量也不是局部变量,称为“非局部的变量(non-local variable)”
一个closure就是一个函数加上该函数所需访问的所有“非局部变量”。通过不同的变量访问函数,将得到一个新的closure。
example
function newCounter ()
local i = 0
return function ()
i = i + 1
return i
end
end


c1 = newCounter() -- 注意后面跟有括号,
c2 = newCounter() 


则c1和c2 是同一函数创建的两个不同closure.


在递归中使用局部函数时,需要注意在局部函数中调用局部函数自身将不能实现。
lua展开局部函数定义的“语法糖”,使用的是局部函数定义:
local foo
foo = function (<参数>)<函数体> end


对于间接递归,必须使用一个明确的前向声明(forward declaration)
proper tail call lua支持“尾调用消除”(tail-call elimination)
当一个函数调用是另一个函数的最后一个行为时,称该调用为“尾调用”
function f(x) return g(x) end
在调用“尾调用”后,程序不再需要保持任何关于该函数的栈信息,当g返回时,执行控制权直接返回到调用f的地方,使得调用g时,f不再耗费任何栈空间.
因此,采用尾调用,将不会导致出现栈溢出的情况。
在lua中,只有 “return <func>(<args>)”的形式才是”尾调用“


7.0 - 7.5
迭代器是一种可以遍历集合中所有元素的机制,在lua中,通常将迭代器表示为函数。可以采用closure来实现。
单纯的closure实现存在需要为每个for循环创建一个closure的问题。


泛型for在循环过程内部保存了迭代器函数,包括3个值 1,一个迭代器函数 2,一个恒定状态, 3,一个控制变量
for <var-list> in <exp-list> do
<body>
end
无状态迭代器不保存自身任何状态,从而可以在多个循环中使用同一个无状态迭代器,避免创建新closure的开销
for循环使用恒定状态和控制变量来调用迭代器,并生产下次迭代的元素
local function iter (a , i)
i = i + 1
local v = a[i]
if v then
return i , v
end
end
function ipairs (a)
return iter,a ,0
end 


for i,v in ipairs(a) do
print(i,v)
end
pairs会以table中得任意次序返回一组值,其迭代器函数是lua中的next。
具有复杂状态的迭代器,可以使用closure,也可以将迭代器所需的状态打包为一个table,保存在恒定状态中。
table中的数据可以修改,但在循环过程中恒定总是同一个table。
在性能上,无状态迭代器优于closure实现的迭代器,closure实现的迭代器优于table实现的迭代器。


8.0 - 8.5
loadfile会从一个文件加载lua代码块,并编译代码,并将编译的结果作为一个函数返回,同时,该函数不会引发错误,它返回错误但不处理错误。
loadfile loadstring(开销比较大) 二个函数的返回值是一个函数。
loadstring总是在全局环境中编译他的字符串。 如果需要对loadstring函数的参数表达式求值,则参数前需要加上return。
load接收一个“读取器函数”,并在内部调用它来获取程序块,一般只在程序块不再文件中,或者程序块过大无法放入内存时使用。
Lua将所有独立的程序块视为一个匿名函数的函数体,且该匿名函数具有可变长实参。
load函数不会引发错误,在错误时,load会返回nil及一条错误消息。
lua中,函数定义是一种赋值操作,是在运行时才完成的操作。


检查平台是否支持动态链接机制, print(package.loadlib("a","b"))然后观察执行结果。
loadlib函数加载指定的库,并将其链接如lua。
require函数会搜索指定的库,再使用loadlib来加载库,并返回初始化函数。初始化函数将库中提供的函数注册到lua中。
assert函数检查第一个参数是否为true,为true,则返回该参数,若为false,则引发一个错误。
若需要在lua中处理错误,使用函数pcall来包装要执行的代码。
当遇到内部错误时,lua会产生错误消息,其他时候,错误消息就是传递给error函数的值。
error函数的第二个附加参数level,用于支出应由调用层级中得那个层来报告当前错误。
xpcall函数第二个参数错误处理函数,可以用来获取调用栈的信息。debug.traceback。


9.0 - 9.1
一个具有多个协同程序的程序在任意时刻只能运行一个协同程序,并且正在运行的协同程序只会在显式的要求挂起(suspend)时,它的执行才会暂停。
Lua将所有关于协同程序的函数放置在一个名为“coroutine”的table中,函数create用于创建新的协同程序。
协同程序有4种不同的状态:挂起(suspend),运行(running),死亡(dead),正常(normal)。
当创建一个协同程序时,它处于挂起状态,可以通过函数status来检查协同程序的状态。
从协同程序的角度看,所有在它挂起时发生的活动都发生在yield调用中,当恢复协同程序的执行时,对yield的调用才最终返回。
resume是在保护模式下运行的,如果一个协同程序的执行中发生任何错误,lua不会显示错误消息,而是将执行权返回给resume函数。
当协同程序a唤醒协同程序b时,协同程序a既不是挂起,也不是运行状态,这时的状态称为“正常”。
通过resume-yield来交换数据:
第一次调用resume时,并没有对应的yield在等待它,因此所有传递给resume的额外参数都将视为协同程序主函数的参数。
co = coroutine.create( function (a,b,c)
print ("co",a,b,c)
end)
coroutine.resume(co,1,2,3) -- co 1 2 3
对resume调用返回的内容,第一个值true表示没有错误,后面的值是对应yield传入的参数
co = coroutine.create ( function (a,b)
coroutine.yield(a + b, a - b)
end)
print(coroutine.resume(co,20,10)) -- true 30 10
与此对应,yield返回的额外值就是对应resume传入的参数:
co = coroutine.create (function ()
print("co",coroutine.yield())
end)
coroutine.resume(co)
coroutine.resume(co,4,5) -- co 4 5
最后一个协同程序结束时,它的主函数返回的值都将作为对应resume的返回值:
co = coroutine.create(function
return 6,7
end)
print(coroutine.resume(co)) -- true 6 7


非对称协同程序(semi-coroutine)存在两个函数来控制协同程序的执行,一个用于挂起,一个恢复执行。





































































  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值