您的位置:首页 > 编程语言 > Lua

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

2017-01-18 16:49 701 查看
lua入门学习   
非等于  ~=

连接号  ..

and  or not   只有 false和nil为假其余为真

a and b -- 如果 a 为 false,则返回 a,否则返回 b
a or b -- 如果 a 为 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 l 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 次 

Capture3是这样一种机制:可以使用模式串的一部分匹配目标串的一部分。将你想捕

获的模式用圆括号括起来,就指定了一个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 库 

简单模式和完全模式

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Lua