脚本语言Lua简单入门学习笔记

lua入门学习   
非等于  ~=
连接号  ..
and  or not   只有 false和nil为假其余为真
and -- 如果 为 false,则返回 a,否则返回 b
or -- 如果 为 true,则返回 a,否则返回 b
表 table
{} 最简单 
days = { "Sunday""Monday""Tuesday""Wednesday",
"Thursday""Friday""Saturday"}
print(days[4])  --> Wednesday   从1开始 而非是0

一个迭代器   --堆结构 先进后出
list =  nil
for 
line  in io.lines()  do
list = {next=list, value=line}
end

l = list
while do
print(l.value)
l = l.next
end

 表中能嵌入表格
初始化
{x=0, y=0} <--> {[ "x"]=0, [ "y"]=0}
{ "red""green""blue"} <-->
{[1]= "red", [2]= "green", [3]= "blue"}

,和;为域分隔符号



基本语法

赋值

遇到赋值语句 Lua 会先计算右边所有的值然后再执行赋值操作,所以我们可以这样
进行交换变量的值:
x, y = y, x  -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i]  -- swap 'a[i]' for 'a[i]'

多值赋值经常用来交换变量,或将函数调用返回给变量:
a, b = f()
f()返回两个值,第一个赋给 a,第二个赋给 b。


局部变量

使用 local 创建一个局部变量,与全局变量不同,局部变量只在被声明的那个代码块
内有效。代码块:指一个控制结构内,一个函数体,或者一个 chunk(变量被声明的那
个文件或者文本串)。

应该尽可能的使用局部变量,有两个好处:
1. 避免命名冲突
2. 访问局部变量的速度比全局变量更快

控制结构语句

控制结构的条件表达式结果可以是任何值,Lua 认为 false 和 nil 为假,其他值为真。
if 语句,有三种形式:

if conditions then
then-part
end;
if conditions then
then-part
else
else-part
end;
if conditions then
then-part
elseif conditions then
elseif-part
..  --->多个  elseif
else
else-part
end;

while 语句:

while condition do
statements;
end;

repeat-until 语句:

repeat
statements;
until conditions;

for 语句有两大类:
第一,数值 for 循环:
for var=exp1,exp2,exp3 do
loop-part
end
如果要退出循环,使用 break 语句

第二,范型 for 循环:
-- print all values of array 'a'
for i,v  in ipairs(a)  do print(v)  end



函数

函数有两种用途:1.完成指定的任务,这种情况下函数作为调用语句使用;2.计算并
返回值,这种情况下函数作为赋值语句的表达式使用。

function f(a, b) return a or b end
CALL PARAMETERS
f(3) a=3, b=nil
f(3, 4) a=3, b=4
f(3, 4, 5) a=3, b=4 (5 is discarded)

Lua 函数中,在 return 后列出要返回的值得列表即可返回多值,如

function maximum (a)
local mi = 1  -- maximum index
local m = a[mi]  -- maximum value
for i,val in ipairs(a) do
if val > m then
mi = i
m = val
end
end
return m, mi
end
print(maximum({8,10,23,12,5}))  --> 23 3

Lua 总是调整函数返回值的个数去适用调用环境,
第一,当作为表达式调用函数时,有以下几种情况:
1. 当调用作为表达式最后一个参数或者仅有一个参数时,根据变量个数函数尽可能
多地返回多个值,不足补 nil,超出舍去。
2. 其他情况下,函数调用仅返回第一个值(如果没有返回值为 nil)
第二,函数调用作为函数参数被调用时,和多值赋值是相同
第三,函数调用在表构造函数中初始化时,和多值赋值时相同。
另外,return f()这种类型的返回 f()返回的所有值
可以使用圆括号强制使调用返回一个值。
函数多值返回的特殊函数 unpack,接受一个数组作为输入参数,返回数组的所有元
素。


可变参数
Lua 函数可以接受可变数目的参数,和 C 语言类似在函数参数列表中使用三点(...)
有时候我们可能需要几个固定参数加上可变参数
function g (a, b, ...) end
有时候需要将函数的可变参数传递给另外的函数调用,可以使用前面我们说过的
unpack(arg)返回 arg 表所有的可变参数


命名参数
Lua 的函数参数是和位置相关的,调用时实参会按顺序依次传给形参
Lua 可以通过将所有的参数放在一个表中
rename{old= "temp.lua", new= "temp1.lua"}
function rename (arg)
return os.rename(arg.old, arg.new)
end 
再论函数

Lua 中的函数是带有词法定界(lexical scoping)的第一类值(first-class values)。
第一类值指:在 Lua 中函数和其他值(数值、字符串)一样,函数可以被存放在变
量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。

词法定界指:被嵌套的函数可以访问他外部函数中的变量。这一特性给 Lua 提供了
强大的编程能力。

既然函数是值,那么表达式也可以创建函数了,Lua 中我们经常这样写:
function foo (x) return 2*x end
下面是原本的函数
foo =  function (x)  return 2*x  end 
  
table 标准库提供一个排序函数
network = {
{name =  "grauna", IP =  "210.26.30.34"},
{name =  "arraial", IP =  "210.26.30.23"},
{name =  "lua", IP =  "210.26.23.12"},
{name =  "derain", IP =  "210.26.23.20"},
}
如果我们想通过表的 name 域排序:
table.sort(network,  function (a,b)
return (a.name > b.name)
end)

将第一类值函数应用在表中是 Lua 实现面向对象和包机制的关键 
闭包

names = { "Peter""Paul""Mary"}
grades = {Mary = 10, Paul = 7, Peter = 8}
function sortbygrade (names, grades)
table.sort(names, function (n1, n2)
return grades[n1] > grades[n2]  -- compare the grades
end)
end

function newCounter()
local i = 0
return function()  -- anonymous function
i = i + 1
return i
end
end
c1 = newCounter()
print(c1())  --> 1
print(c1())  --> 2
c2 = newCounter()
print(c2())  --> 1
print(c1())  --> 3
print(c2())  --> 2

返回一个函数就闭包?拿到引用和不拿到引用的两种情况??

非全局函数

Lua 中函数可以作为全局变量也可以作为局部变量
函数作为 table 的域(大部分 Lua 标准库使用这种机制来实现的比如 io.read、math.sin)
1. 表和函数放在一起
Lib = {}
Lib.foo =  function (x,y)  return x + y  end
Lib.goo =  function (x,y)  return x - y  end
2. 使用表构造函数
Lib = {
foo =  function (x,y)  return x + y  end,
goo =  function (x,y)  return x - y  end
}
3. Lua 提供另一种语法方式
Lib = {}
function Lib.foo (x,y)
return x + y
end
function 
Lib.goo (x,y)
return x - y
end

当我们将函数保存在一个局部变量内时,我们得到一个局部函数,也就是说局部函
数像局部变量一样在一定范围内有效
方式一
local f = function (...)
...
end
local g = function (...)
...
f()  -- external local `f' is visible here
...
end

方式二
local function f (...)
...
end
声明递归局部函数的方式
local fact
fact = function (n)
if n == 0 then
return 1
else
return n*fact(n-1)
end
end






正确的尾调用

尾调用是一种类似在函数结尾的 goto 调用, 当函数最后一个动作是调用另外一个函
数时
,我们称这种调用尾调用。例如:
function f(x)
return g(x)
end 

例子中 f 调用 g 后不会再做任何事情,这种情况下当被调用函数 g 结束时程序不需
要返回到调用者 f;所以尾调用之后程序不需要在栈中保留关于调用者的任何信息
由于尾调用不需要使用栈空间,那么尾调用递归的层次可以无限制的。

Lua 中类似 return g(...)这种格式的调用是尾调用。但是 g 和 g 的参数都可以是复杂
表达式,因为 Lua 会在调用之前计算表达式的值。例如下面的调用是尾调用:
return x[i].foo(x[j] + a*b, i + j)







迭代器与泛型 for 

使用闭包来简单实现  
function list_iter (t)
local i = 0
local n = table.getn(t)
return function ()
i = i + 1
if i <= n then return t[i] end
end
end

使用例子:
t = {10, 20, 30}
iter = list_iter(t)  -- creates the iterator
while true do
local 
element = iter()  -- calls the iterator
if element ==  nil then break end
print(element) 
end
  
例子二: 范式for

t = {10, 20, 30}
for element  in list_iter(t)  do
print(element)
end


范性 for 的语义

范性 for 的执行过程:
首先,初始化,计算 in 后面表达式的值,表达式应该返回范性 for 需要的三个值:
迭代函数,状态常量和控制变量;与多值赋值一样,如果表达式返回的结果个数不足三
个会自动用 nil 补足,多出部分会被忽略。
第二,将状态常量和控制变量作为参数调用迭代函数(注意:对于 for 结构来说,
状态常量没有用处,仅仅在初始化时获取他的值并传递给迭代函数)。
第三,将迭代函数返回的值赋给变量列表。
第四,如果返回的第一个值为 nil 循环结束,否则执行循环体。
第五,回到第二步再次调用迭代函数。
更精确的来说:
for var_1, ..., var_n in explist do block end
等价于
do
local _f, _s, _var = explist
while true do
local var_1, ... , var_n = _f(_s, _var)
_var = var_1
if _var == nil then break end
block
end
end
如果我们的迭代函数是 f,状态常量是 s,控制变量的初始值是 a0,那么控制变量将
循环:a1=f(s,a0)、a2=f(s,a1)、……,直到 ai=nil。


无状态的迭代器

多状态的迭代器
将 table作为迭代器的状态常量

真正的迭代器 

函数作为参数  
local count = 0
allwords(function (w)
if w ==  "hello" then count = count + 1 end
end)
print(count)

编译· 运行· 调试

function dofile (filename)
local f = assert(loadfile(filename))
return f()
end

loadstring 与 loadfile 相似,只不过它不是从文件里读入 chunk,而是从一个串中读入。
例如:
f = loadstring( "i = i + 1")
f 将是一个函数,调用时执行 i=i+1。
i = 0
f(); print(i)  --> 1
f(); print(i)  --> 2

调用别的包里内容:
-- file `foo.lua'
function foo (x)
print(x)
end

f = loadfile("foo.lua")后,foo 被编译了但还没有被定义,如果要定
义他必须运行 chunk:
f()  -- defines `foo'
foo( "ok"--> ok

如果你想快捷的调用 dostring(比如加载并运行),可以这样
loadstring(s)()

大概与 f = function () i = i + 1 end 等价,但是第二段代码速度更快因为它只需要编译
一次,第一段代码每次调用 loadstring 都会重新编译,还有一个重要区别:loadstring 编
译的时候不关心词法范围:
local i = 0
f = loadstring( "i = i + 1")
g = function () i = i + 1 end
这个例子中,和想象的一样 g 使用局部变量 i,然而 f 使用全局变量 i;loadstring 总
是在全局环境中编译他的串


require 函数

Lua 提供高级的 require 函数来加载运行库。粗略的说 require 和 dofile 完成同样的功
能但有两点不同:
1. require 会搜索目录加载文件
2. require 会判断是否文件已经加载避免重复加载同一文件。由于上述特征, require
在 Lua 中是加载库的更好的函数。

异常和错误处理
如果在 Lua 中需要处理错误,需要使用 pcall 函数封装你的代码。
第一步:将这段代码封装在一个函数内
function foo ()
...
if unexpected_condition then error() end
...
print(a[i])  -- potential error: `a' may not be a table
...
end
第二步:使用 pcall 调用这个函数
if pcall(foo) then
-- no errors while running `foo'
...
else
-- `foo' raised an error: take appropriate actions
...
end
当然也可以用匿名函数的方式调用 pcall:
if pcall(function () ... end) then ...
else ...




协同程序

协同程序(coroutine)与多线程情况下的线程比较类似:有自己的堆栈,自己的局
部变量,有自己的指令指针,但是和其他协同程序共享全局变量等很多信息。线程和协
同程序的主要不同在于:在多处理器情况下,从概念上来讲多线程程序同时运行多个线
程;而协同程序是通过协作来完成,在任一指定时刻只有一个协同程序在运行,并且这
个正在运行的协同程序只有在明确的被要求挂起的时候才会被挂起


协同的基础

协同有三个状态:挂起态、运行态、停止态。当我们创建一个协同程序时他开始的
状态为挂起态,也就是说我们创建协同程序的时候不会自动运行,可以使用 status 函数
检查协同的状态:
co = coroutine.create( function ()
print( "hi")
end)
print(co)  --> thread: 0x8071d98
print(coroutine.status(co))  --> suspended
函数 coroutine.resume 可以使程序由挂起状态变为运行态:
coroutine.resume(co)  --> hi
这个例子中,协同体仅仅打印出"hi"之后便进入终止状态:
print(coroutine.status(co))  --> dead


管道和过滤器
协同最有代表性的作用是用来描述生产者-消费者问题。我们假定有一个函数在不断
的生产值(比如从文件中读取),另一个函数不断的消费这些值(比如写到另一文件中),
这两个函数如下:

function producer ()
while true do
local x = io.read()  -- produce new value
send(x)  -- send to consumer
end
end


function consumer ()
while true do
local x = receive()  -- receive from producer
io.write(x,  "\n"-- consume new value
end
end

function receive ()
local status, value = coroutine.resume(producer)
return value
end
function send (x)
coroutine.yield(x)
end
producer = coroutine.create( function ()
while true do
local x = io.read()  -- produce new value
send(x)
end
end)
这种设计下,开始时调用消费者,当消费者需要值时他唤起生产者生产值,生产者
生产值后停止直到消费者再次请求。我们称这种设计为消费者驱动的设计

过滤器在同一时间既是生产者又是消费者,他请求生产者生产值并
且转换格式后传给消费者,我们修改上面的代码加入过滤器(每一行前面加上行号) 。完
整的代码如下:

function receive (prod)
local status, value = coroutine.resume(prod)
return value
end
function send (x)
coroutine.yield(x)
end
function producer ()
return coroutine.create(function ()
while true do
local x = io.read()  -- produce new value
send(x)
end
end)
end
function filter (prod)
return coroutine.create(function ()
local line = 1
while true do
local x = receive(prod)  -- get new value
x = string.format( "%5d %s", line, x)
send(x)  -- send it to consumer
line = line + 1
end
end)
end
function consumer (prod)
while true do
local x = receive(prod)  -- get new value
io.write(x,  "\n"-- consume new value
end
end
可以调用:
p = producer()
f = filter(p)
consumer(f)
或者:
consumer(filter(producer()))

用作迭代器的协同


非抢占式多线程 
完整示例

马尔可夫链算法



第二篇 tables 与 objects

数据结构
table 是 Lua 中唯一的数据结构,其他语言所提供的其他数据结构比如:arrays、
records、lists、queues、sets 等,Lua 都是通过 table 来实现,并且在 lua 中 table 很好的实现了这些数据结构。

数组 
  
通常我们初始化数组的时候就间接的定义了数组的大小,比如下面的代码:
a = {}  -- new array
for i=1, 1000  do
a[i] = 0
end

squares = {1, 4, 9, 16, 25, 36, 49, 64, 81}
这样的语句中数组的大小可以任意的大,甚至几百万。

阵和多维数组
Lua 中主要有两种表示矩阵的方法,第一种是用数组的数组表示。也就是说一个表
的元素是另一个表。例如,可以使用下面代码创建一个 n 行 m 列的矩阵:
mt = {}  -- create the matrix
for i=1,N  do
mt[i] = {}  -- create a new row
for j=1,M  do
mt[i][j] = 0
end
end


链表



字符串缓冲

这个问题并不是 Lua 特有的:其它的采用垃圾收集算法的并且字符串不可变的语言
也都存在这个问题。Java 是最著名的例子,Java 专门提供 StringBuffer 来改善这种情况。



Metatables and Metamethods 


默认创建一个不带 metatable 的新表
t = {}
print(getmetatable(t))  --> nil
可以使用 setmetatable 函数设置或者改变一个表的 metatable
t1 = {}
setmetatable(t, t1)
assert(getmetatable(t) == t1) 
  
任何一个表都可以是其他一个表的 metatable,一组相关的表可以共享一个 metatable
(描述他们共同的行为)。一个表也可以是自身的 metatable(描述其私有行为)。


算术运算的 Metamethods

首先,我们实现一个原型和一
个构造函数,他们共享一个 metatable:
-- create a namespace
Window = {}
-- create the prototype with default values
Window.prototype = {x=0, y=0, width=100, height=100, }
-- create a metatable
Window.mt = {}
-- declare the constructor function
function Window.new (o)
setmetatable(o, Window.mt)
return o
end


环境  

为了简化操作, Lua  将环境本身存储在一个全局变量 _G  中,( _G._G 
_G )。例如,下面代码打印在当前环境中所有的全局变量的名字:
for  n  in  pairs(_G)  do  print(n)  end
这一章我们将讨论一些如何操纵环境的有用的技术。  

使用动态名字访问全局变量  





Packages  

Lua  并没有提供明确的机制来实现  packages 。然而,我们通过语言提供的基本的机
制很容易实现他。主要的思想是:像标准库一样,使用表来描述 
package  

基本方法 
   
complex = {}
function  complex.new (r, i)  return  {r=r, i=i}  end
-- defines a constant `i'
complex.i = complex.new(0, 1)  

私有成员( Privacy  

我们可以将  package  内的所有函数都声明为局部的,最后将他们放在最终的表中。按照这种方法,上面的  complex package 修改如下:  

local function  checkComplex (c)
if not  ((type(c) ==  "table" )
and  tonumber(c.r)  and  tonumber(c.i))  then
error( "bad complex number" , 3)
end  

end
local function 
new (r, i)  return  {r=r, i=i}  end
local function 
add (c1, c2)
checkComplex(c1);
checkComplex(c2);
return  new(c1.r + c2.r, c1.i + c2.i)
end
...
complex = {
new = new,
add = add,
sub = sub,
mul = mul,
div = div,
}
 


包与文件 
   
require "four";

面向对象程序设计  
Lua  中的表不仅在某种意义上是一种对象。像对象一样,表也有状态(成员变量);
也有与对象的值独立的本性,特别是拥有两个不同值的对象(
table )代表两个不同的对
象;一个对象在不同的时候也可以有不同的值,但他始终是一个对象;与对象类似,表
的生命周期与其由什么创建、在哪创建没有关系。对象有他们的成员函数,表也有:
Account = {balance = 0}
function  Account.withdraw (v)
Account.balance = Account.balance - v
end
这个定义创建了一个新的函数,并且保存在  Account  对象的  withdraw  域内,下面我
们可以这样调用:
Account.withdraw(100.00)  

一个灵活的方法是:定义方法的时候带上一个额外的参数,来表示方法作用的对象。
这个参数经常为 
self  或者  this
function  Account.withdraw (self, v)
self.balance = self.balance - v
end
现在,当我们调用这个方法的时候不需要指定他操作的对象了:
a1 = Account; Account =  nil
...
a1.withdraw(a1, 100.00) 
-- OK  

Lua  也提供了通
过使用冒号操作符来隐藏这个参数的声明。我们可以重写上面的代码:
function  Account:withdraw (v)
self.balance = self.balance - v
end
调用方法如下:
a:withdraw(100.00)  

 
Lua  不存在类的概念,每个对象定义他自己的行为并拥有自己的形状  
  Lua  中,使用前面章节我们介绍过的继承的思想,很容易实现  prototypes. 更明确
的来说,如果我们有两个对象 
a    b ,我们想让  b  作为  a    prototype  只需要:
setmetatable(a, {__index = b})  

继承  

假定我们有一个基类  Account
Account = {balance = 0}
function  Account:new (o)
o = o 
or  {}
setmetatable(o, self)
self.__index = self
return  o
end
function 
Account:deposit (v)
self.balance = self.balance + v
end
function 
Account:withdraw (v)
if  v > self.balance  then  error "insufficient funds"  end
self.balance = self.balance - v
end
我们打算从基类派生出一个子类  SpecialAccount ,这个子类允许客户取款超过它的
存款余额限制,我们从一个空类开始,从基类继承所有操作:
SpecialAccount = Account:new()  

到现在为止, SpecialAccount  仅仅是  Account  的一个实例。现在奇妙的事情发生了:
s = SpecialAccount:new{limit=1000.00}
SpecialAccount    Account  继承了  new  方法,当  new  执行的时候, self  参数指向
SpecialAccount 。所以, s    metatable    SpecialAccount __index  也是  SpecialAccount
这样,
s  继承了  SpecialAccount ,后者继承了  Account 。当我们执行:
s:deposit(100.00)
Lua    s  中找不到  deposit  域,他会到  SpecialAccount  中查找,在  SpecialAccount 
找不到,会到 
Account  中查找。使得  SpecialAccount  特殊之处在于,它可以重定义从父
类中继承来的方法:
function  SpecialAccount:withdraw (v)
if  v - self.balance >= self:getLimit()  then
error "insufficient funds"
end
self.balance = self.balance - v
end
function 
SpecialAccount:getLimit ()
return  self.limit  or  0
end  


多重继承  

实现的关键在于:将函数用作 __index 。记住,当一个表的  metatable  存在一个 __index
函数时,如果  Lua  调用一个原始表中不存在的函数,  Lua  将调用这个 __index  指定的函数。这样可以用 __index  实现在多个父类中查找子类不存在的域。  

Single-Method  的对象实现方法  

一个保存状态的迭代子函数就是一个  single-method  对象。
function  newObject (value)
return function  (action, v)
if  action ==  "get"  then return  value
elseif  action ==  "set"  then  value = v
else  error( "invalid action" )
end
end
end
使用起来很简单:
d = newObject(0)
print(d(
"get" ))  --> 0
d( "set" , 10)
print(d(
"get" ))  --> 10  




数学库  

Table   

另一个有用的函数是  table.sort 。他有两个参数:存放元素的  array  和排序函数。排序
函数有两个参数并且如果在 
array  中排序后第一个参数在第二个参数前面,排序函数必
须返回 
true 。如果未提供排序函数, sort  使用默认的小于操作符进行比较。  

String   

记住: Lua  中的字符串是恒定不变的。 String.sub  函数以及  Lua  中其他的字符串操作
函数都不会改变字符串的值,而是返回一个新的字符串。
 

下面的表列出了  Lua  支持的所有字符类:
.  任意字符
%a  字母
%c  控制字符
%d  数字
%l  小写字母
%p  标点字符
%s  空白符
%u  大写字母
%w  字母和数字
%x  十六进制数字
%z  代表  0  的字符  
上面字符类的大写形式表示小写所代表的集合的补集。例如, '%A' 非字母的字符:  
print(string.gsub( "hello, up-down!" ,  "%A" ,  "." ))
--> hello..up.down. 4  
可以使用修饰符来修饰模式增强模式的表达能力, Lua  中的模式修饰符有四个:
+  匹配前一字符  1  次或多次
*  匹配前一字符  0  次或多次
-  匹配前一字符  0  次或多次
?  匹配前一字符  0  次或  1   


Capture 3 是这样一种机制:可以使用模式串的一部分匹配目标串的一部分。将你想捕
获的模式用圆括号括起来,就指定了一个
capture
 
string.find  使用  captures  的时候,函数会返回捕获的值作为额外的结果。这常被用
来将一个目标串拆分成多个:
pair =  "name = Anna"
_, _, key, value = string.find(pair,  "(%a+)%s*=%s*(%a+)" )
print(key, value) 
--> name Anna 
   
%d+'  匹配一个或多个数字(整数):
i, j = string.find( "the number 1298 is even" ,  "%d+" )
print(i,j) 
--> 12 15  


IO   

简单模式和完全模式

获取或者设置文件句柄后进行的操作 --


你可能感兴趣的:(脚本语言)