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

Lua基本语法总结

2014-04-14 11:53 489 查看



原文链接 点击打开链接

http://www.cnblogs.com/summericeyl/archive/2011/06/02/2068507.html

Lua笔记 第一至第八章

Lua特性: 可扩展性, 简单, 高效率, 与平台无关

可通过新类型和函数来扩展其功能.


1.1 Chunks

Chunk是一系列语句
lua交互运行, CTRL-Z退出

lua -la -lb

先运行a, 然后运行b(-l选项会调用require)

-i选项要求Lua运行指定Chunk后进入交互模式
dofile函数, 连接外部Chunk


1.2 全局变量

赋值即创建, 未初始化的值为nil


1.3 词法约定

保留字: and break do else elseif end false for function if in local nil not or repeat return then true until while
Lua 大小写敏感
单行注释: --
多行注释: --[ [ --]]


1.4 命令行方式

lua [options] [script [args]]

-e 直接将命令传入Lua

lua -e "print(math.sin(12))"

-l: 加载一个文件
-i: 进入交互模式
_PROMPT 设置一个标量作为交互模式的提示符
lua -i -e "_PROMPT=' lua> '"


LUA_INIT环境变量: @filename 表示加载指定文件. 如无@, 则假定filename为Lua代码文件并且运行它
脚本名索引为0, 参数从1开始增加.
lua -e "sin=math.sin" script a b
arg[-3] = "lua"
arg[-2] = "-e"
arg[-1] = "sin=math.sin"
arg[0] = "script"
arg[1] = "a"
arg[2] = "b"


第2章 类型和值

八个基本类型: nil, boolean, number, string, userdata, function, thread, table


2.1 Nil

全局变量未被赋值之前


2.2 Booleans

Lua所有的值都可以作为条件, 除了false和nil为假, 其他都为真


2.3 Number

没有整数


2.4 Strings

可使用单引号或双引号表示字符串
转义符序列: \b 后退 \f 换页 \n 换行 \r 回车 \t 制表 ...

\[ \] 左中/左右括号
还可以使用 [ [...]] 表示字符串, 这样可以多行, 可以嵌套也不会解释转义序列
运行时, Lua自动在 string 和 numbers 之间自动进行类型转换, 使用算术操作符, string自动转换成数字, 或者其他需要字符串的时候, 数字转换成字符串
string转换成数字函数 tonumber(), 相反则为 tostring


2.5 Functions

函数可以作为函数的参数, 也可以作为函数的返回值.
Lua可以调用lua或者C实现的函数, 标准库包括 string库, table库, I/O库, OS库, 算术库, debug库


2.6 Userdata and Threads

userdata可以将C数据存放在Lua变量中, userdata除了赋值和相等比较外没有预定义的操作.
userdata用来描述应用程序或者使用C实现的库创建的新类型.

第3章 表达式


3.1 算术表达式

二元: + - * / ^(加减乘除幂)
一元: - 负值


3.2 关系运算符

< > <= >= == ~=

回 false和true

nil只能和自己相等
通过引用比较tables, userdata, functions, 即仅当两者表示同一个对象时相等
比较字符串按字母顺序比较

注: "2" < "15" 返回 false 字母顺序


3.3 逻辑运算发

and or not
只有 false 和 nil为假, 其他为真, 0也为真
and 的优先级比 or高


3.4 连接运算符

.. ---- 字符串连接


3.5 优先级

^
not -
* /
+ -
..
< > <= >= ~= ==
and
or

除了^和..之外, 所有二元运算符都是左连接的


3.6 表的结构

最简单的构造函数是{}, 用来创建一个空表, 可以直接初始化数组
days = {"Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}


第一个元素索引为1

即"Sunday" = days[1]

元素可以为表达式
初始化一个表为record
a = {x = 0, y = 0}            <-->    a={}; a.x=0; a.y=0


不管何种方式创建table, 我们都可以向表中添加或者删除任何类型的域, 构造函数仅仅影响表的初始化
给元素赋值nil可以移除该元素
每次调用构造函数, Lua都会创建一个新的table, 可以使用table构造一个list
在同一个构造函数中可以混合列表风格和record风格进行初始化

用[expression]显示的表示将被初始化的索引
opnames = {["+"] = "add", ["-"] = "sub",  ["*"] = "mul", ["/"] = "div"}
{x = 0, y = 0}  <-->  {["x"] = 0, ["y"] = 0}
{"red", "green", "blue"}  <-->  {[1]= "red", [2] = "green", [3] = "blue"}


如果想从下标0开始
days = { [0] = "Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}


构造函数中域分隔符可用"," 或者";"表示, 一般";"分割不同类型的表元素

第4章 基本语法


4.1 赋值语句

可同时对多个变量同时赋值
a, b = 10, 2*x   <-->   a = 10; b = 2*x


先计算右边所有的值, 然后再进行赋值操作, 所以可以进行交换
x, y = y, x
a[i], a[j] = a[j], a[i]


当变量个数和值的个数不一致

a. 变量个数 > 值的个数 按变量个数补足nil
b. 变量个数 < 值的个数 多余的值会被忽略

函数返回值

a, b = f()

f()如果返回两个值, 第一个赋值给a, 第二个赋值给b


4.2 局部变量和代码块(block)

local 创建一个局部变量, 与全局变量不同, 局部变量只在被声明的那个代码块内有效.
代码块: 一个控制结构内, 一个函数体, 或者一个chunk(变量被声明的那个文件或者文本串)
尽可能使用局部变量: 1. 避免命名冲突, 2. 访问局部变量比全局变量快


4.3 控制结构语句

if语句, 有三种形式:
if condition then
then-part
end;

if condition 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
statement;
end;


repeat-until 语句
repeat
statements;
until conditions


for 语句
两大类:

第一, 数值 for 循环
for var = exp1, exp2, exp3 do
loop-part
end


exp1(初始值), exp2(终止值), exp3表示进行的计算, 默认exp3 step = 1
控制变量var是局部变量自动被声明, 并且只在循环内有效
退出循环用break语句

第二, 范型for循环
for i, v in ipairs(a) do print(v) end


范型for遍历迭代子函数返回的每一个值.


4.4 break和return语句

break 退出当前循环
return 函数返回结果.
break和return只能出现在block的结尾一句
如果要在中间使用, 可以使用 do..end来实现


第5章 函数

语法:
function func_name (arguments-list)
statements-list;
end;


当函数只有一个参数并且这个参数是字符串或者表结构的时候, ()可有可无
print "Hello world"
dofile "a.lue"
-- 打印多行 --
print [[a multi-line message]]
f{x = 10, y = 20}
type{}


面向对象调用函数的语法, 如 o:foo(x)和o.foo(o,x) 是等价的


5.1 多返回值

Lua 函数中, 在return后列出要返回的值列表即可返回多值, 如: return m, m1
注意返回值的几个情况:

赋值语句, 如果调用函数表达式作为最后一个参数或者仅有一个参数, 根据变量个数函数尽可能返回多个值, 不足补nil
其他情况, 函数调用仅返回第一个值
例如 foo2() 返回 'a', 'b'

x, y = foo2(), 20 -- x = 'a', y =20

另外, 函数调用表达式作为函数参数被调用的时候, 和多值赋值是相同的
print(foo2())        --> a b
print(foo2(), 1)        --> a 1
print(foo2().."x")        --> ax


函数调用表达式在表构造函数中初始化时, 和多值赋值时相同
a = {foo2()}        -- a = {'a', 'b'}
a = {foo2(), 4}        -- a = {'a', 4}


return f()表示返回"f()的返回值", 使用圆括号强制返回一个值
print(foo2())        --> a b
print((foo2()))        --> a


一个return语句如果使用圆括号将返回值括起来也将导致返回一个值
函数多值返回的特殊函数unpack, 接受一个数组作为输入参数, 返回数组的所有元素

Lua中如果想调用可变参数的可变函数只需要: f(unpack(a))


5.2 可变参数

Lua 和C语言类似在函数参数列表中使用三点(...)表示函数有可变的参数
Lua将函数的参数放在一个叫arg的表中, 除了参数之外, arg表中还有一个域n表示参数的个数
需要几个固定参数加上可变参数
function g(a, b, ...) end
g(3)                a = 3, b = nil, arg = {n=0}
g(3,4)                a = 3, b = 4, arg = {n=0}
g(3,4, 5, 8)                a = 3, b = 4, arg = {n=2}


哑元: 下划线
local _, x = string.find(s, p)


函数 string.format: 文本格式化, 类似C的sprintf函数


5.3 命名函数

第6章 再论函数

函数可以没有名字, 匿名的.函数名实际上是一个指向函数的变量
a = {p = print}
a.p("Hello world")
print = math.sin
a.p(print(1))
sin = a.p
sin(10, 20)


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" },
}
-- 进行排序: --
table.sort(network, function(a, b)
return (a.name > b.name)
end)



6.1 闭包

当一个函数内部嵌套另一个函数定义时, 内部的函数体可以访问外部的函数的局部变量
names = {"Peter", "Paul", "Mary"}
grades = {Mary = 10, Paul = 7, Peter = 8}
table.sort(names, function (n1, n2)
return grades[n1] > grades[n2]
end)


创建函数实现该功能:
function sortbygrade(names, grades)
table.sort(names, function(n1, n2)
return grades[n1] > grades[n2]
end)
end


在函数内部的匿名函数访问的变量为外部的局部变量或者upvalue


6.2 非全局函数

表和函数放在一起
Lib = {}
Lib.foo = function(x, y) return x+y end
Lib.goo = function(x, y) return x-y end


使用表构造函数
Lib = {
Lib.foo = function(x, y) return x+y end
Lib.goo = function(x, y) return x-y end
}


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()
...
end


方式二
local function f(...)
...
end


使用非递归局部函数是要先定义而后使用


6.3 正确的尾调用

函数的最后一个动作是调用另外一个函数时, 我们称这种调用为尾调用
function f(x)
return g(x)
end


尾调用不需要使用栈空间, 所以尾调用递归的层次可以无限制的
可以将尾调用理解成一种goto
见状态机, 房间四个门的例子, 适合于游戏中寻路

第7章 迭代器与范型

闭包是一个内部函数, 它可以访问一个或者多个外部函数的外部局部变量. 每次闭包的成功调用后这些外部局部变量都保存他们的值(状态)

一个典型的闭包的结构包含两个函数: 一个是闭包自己, 另一个是工厂(创建闭包的函数)
我们为list写一个简单的迭代器
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


list_iter是一个工厂, 每次调用都会调用一个新的闭包(迭代器本身). 闭包保存内部局部变量(t, i, n)
当list中没有值时, 返回nil
t = {10, 20, 30}
iter = list_iter(t)
while true do
local element = iter()
if element == nil then break end
print(element)
end


用于范性for语句
t = {10, 20, 30}
for element in list_iter(t) do
print(element)
end


这里为迭代循环处理所有的蒲记(bookkeeping): 首先调用迭代工厂; 内部保留迭代函数, 因为我们不需要iter变量; 然后在每一个新的迭代处调用迭代器函数, 当迭代器返回nil时循环结束


7.2 范型 for 的语义

for <var-list> in <exp-list> do
<body>
end


var-list 是以一个或多个逗号分隔的变量名列表. <exp-list> 是以一个或多个逗号分隔的表达式列表, 通常情况下exp-list只有一个值: 迭代工厂的调用
for k,v in pairs(t) do
print(k, v)
end


也可以只有一个变量
for line in io.lines() do
io.write(line, '\n')
end


我们称第一个变量为控制变量, 其值为nil时循环结束
下面我们看看范型for的执行过程

首先, 初始化, 计算in后面表达式的值, 表达式应该返回范性for需要的三个值: 迭代函数, 状态常量, 控制变量. 如返回的结果不足三个会自动用nil补足
第二, 将状态常量和控制变量作为参数调用迭代函数(对for结构来说, 状态常量没有用处, 仅仅在初始化时获取他的值并传递哦给迭代函数)
第三, 将迭代函数返回的值赋给变量列表
第四, 如果返回的第一个值为nil循环结束, 否则执行循环体
第五, 回到第二步再次调用迭代函数

如我们的迭代函数是f, 状态常量是s, 控制变量的初始值为a0, 则循环: a1 = f(s, a0), a2 = f(s, a1), ... 知道 ai = nil


7.3 无状态的迭代器

无状态迭代器是指不保存任何状态的迭代器, 在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价
每一次迭代, 迭代器都是用两个变量(状态变量和控制变量)的值作为参数被调用, 一个无状态的迭代器只利用这两个值可以获取下一个元素
a = {"one", "two", "three"}
for i, v in ipairs(a) do
print(i, v)
end


ipairs的实现
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



7.4 多状态的迭代器

迭代器保存多个状态信息, 最简单的方法是用闭包, 还有一种方法就是将所有的状态信息封装到table内, 将table作为迭代器的状态常量.
local iterator
function allwords()
local state = {line = io.read(), pos = 1}
return iterator, state
end
function iterator (state)
while (state.line)
local s, e = string.find(state.line, "%w+", state.pos)
if a then
state.pos = e + 1
return string.sub(state.line, s, e)
else
state.line = io.read()
state.pos = 1
end
end
return nil
end


尽可能使用无状态迭代器, 不行则尽可能使用闭包, 尽量不要使用table这种方式


7.5 真正的迭代器

function allwords (f)
for l in io.lines() do
for w in string.gfind(l, "%w+") do
f(w)
end
end
end


打印单词 allwords(print)
匿名函数
local count = 0
allwords(function (w)
if w == "hello" then count = count + 1 end
end)
print(count)
for循环
local count = 0
for w in allwords() do
if w == "hello" then count = count + 1
end
print(count)


第8章 编译.运行.错误信息

完全简单的功能dofile比较方便, 读入文件编译并且执行.
loadfile读取文件编译但不执行, 如发生错误, 返回nil和错误信息, loadfile只需要编译一次, 但可多次运行. dofile每次都要编译
loadstring和loadfile类似, 只是它从一个串中读入
f = loadstring("i = i + 1")
i = 0
f()    -->执行该代码  i=1
f()       --> i=2


Lua把每一个chunk都作为一个匿名函数处理, chunks可以定义局部变量也可以返回值
f = loadstring("local a = 10; return a + 20")
print(f())


loadstring和loadfile都不会抛出错误, 如果发生错误他们将返回nil加上错误信息
如果你想快捷的调用dostring, 可以这样: loadstring(s)()
为了返回更清楚的错误信息可以使用assert: assert(loadstring(s))()
使用loadstring, 可以只编译一次, 多次使用, 另外它不关心词法范围


8.1 require 函数

require函数加载运行库. 与dofile完成相同的功能, 但有两点不同:

require 会搜索目录加载文件
require 会判断是否文件已经加载避免重复加载同一文件.

require的路径是一个模式列表, 例如: ?;?.lua;c:\windows\?;/user/local/lua/?/?.lua

为了确定路径, Lua首先检查全局变量LUA_PATH是否为一个字符串, 而后判断这个字符串是否为路径. 如果失败则使用固定的路径 "?;?.lua"
Lua保留一张所有已加载的文件列表, 全局变量_LOADED访问文件名列表, 通过设置其值为nil, 可以让require重新加载相同的文件, 比如文件"foo"
_LOADED["foo"] = nil


路径模式可以不包含问号, 而只是一个固定的路劲: ?;?.lua;/usr/local/default.lua
_REQUIREDNAME保存被required的虚文件的名称

如, 我们把路径设置为 "/usr/local/default.lua", 这样每次调用require都会运行newrequire.lua, 然后在使用_REQUIREDNAME的值实际加载required的文件


8.2 C Package

在Lua提示符下运行print(loadlib())看返回的结果, 如果显示bad arguments则说明你的发布版本支持动态连接机制, 否则说明动态连接机制不支持或者没有安装
Lua的loadlib函数内提供了所有动态连接的功能, 有两个参数: 库的绝对路径和初始化函数
local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")


加载指定的库, 并不打开库(即没有调用初始化函数), 返回初始化函数作为Lua的一个函数
f()                --- 打开库



8.3 错误

你可以通过调用error函数显示地抛出错误, error的参数是要抛出的错误信息
print "enter a number:"
n = io.read("*number")
if not n then error("invalid input") end


Lua提供了专门的内置函数来完成上面类似的功能
print "enter a number:"
n = assert(io.read("*number"), "invalid input")


assert 首先检查第一个参数, 若没问题, assert不作任何事情, 否则, assert以第二个参数作为错误信息抛出. 第二个参数是可选的
对于程序逻辑上能够避免的异常, 以抛出错误的方式处理之, 否则返回错误代码


8.4 异常和错误处理

如果在lua中需要处理错误, 使用pcall函数封装你的代码
pcall在保护模式下执行函数内容, 同时捕获所有的异常和错误, 若一切正常, 返回true以及"被执行函数"的返回值, 否则返回nil和错误信息
传递给error的任何信息都会被pcall返回


8.5 错误信息和回跟踪(Tracebacks)

不管什么情况下, Lua都尽可能清楚的描述问题发生的缘由
函数error还可以有第二个参数, 表示错误发生的层级
cpcall实现功能, 两个参数: 调用函数, 错误处理函数
最常用的debug处理函数: debug.debug和debug.traceback



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