lua快速入门 —— 基本语法

本文最后更新于:2020年2月29日 下午

概览:Lua的语法规则、变量类型。

参照书籍:《Lua程序设计(第二版)》

Lua版本:Lua 5.3.5

lua的语法规则

关键字

1
2
3
4
5
6
7
8
9
and    break   do     else    elseif

end false for function if

in local nil not or

repeat return then true until

while

标识符

字母或者下划线开头的字母、下划线、数字序列。(与C语言相同)

最好不要使用下划线加大写字母的标识符(可能会与lua的保留字冲突)

lua是大小写敏感的!

变量

lua的变量不需要声明。

给一个变量赋值后就会创建这个变量

当访问一个没有初始化的全局变量时也不会出错,得到值为 nil

1
print(b)  -- nil

需要注意的是:lua中的变量如果没有特殊说明,全是全局变量,那怕是语句块或是函数里。变量前加local关键字的是局部变量。

注释

1
2
3
4
5
6
7
print(type("aa"))  --print "aa" type is string

--[[

多行注释

--]]

变量类型

Lua是动态类型的语言,变量不需要类型定义。

Lua中共有8种基本类型:nil、boolean、number、string、userdata、function、thread、table.

使用函数type()可以查看变量的类型

1
2
3
4
5
6
7
8
9
10
11
12
13
print(type("Hello world"))      --> string

print(type(10.4*3)) --> number

print(type(print)) --> function

print(type(type)) --> function

print(type(true)) --> boolean

print(type(nil)) --> nil

print(type(type(X))) --> string

变量类型

nil(空)

Lua中特殊的类型,他只有一个值:nil;

一个全局变量没有被赋值以前默认值为nil;

给全局变量赋值nil可以删除该变量。

boolean(布尔)

两个取值false和true。

但要注意Lua中所有的值都可以作为条件

在控制结构的条件中除了false和nil为假,其他值都为真。

所以Lua认为0和空串都是真。

number (双精度浮点数)

Lua中没有整数,数字只有这一种类型

数字常量的例子:

1
4   0.4  4.57e-3    0.3e12   5e+20

string (字符串)

  1. lua中字符串可以包含任何字符。(lua是8位字节)

  2. Lua中字符串不允许被修改。

  3. Lua的string是由Lua自动进行内存分配和释放。一个string可以只包含一个字母也可以包含一本书,Lua可以高效的处理长字符串,1M的string在Lua中是很常见的

  4. 字符串可用单引号或者双引号括起来表示字符串

  5. Lua支持转义字符

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    \a bell

    \b back space -- 后退

    \f form feed -- 换页

    \n newline -- 换行

    \r carriage return -- 回车

    \t horizontal tab -- 制表

    \v vertical tab

    \\ backslash -- "\"

    \" double quote -- 双引号

    \' single quote -- 单引号

    \[ left square bracket -- 左中括号

    \] right square bracket -- 右中括号
  6. string支持通过数值来指定字符串中的字符

    如:“alo” 可以用”\97lo“表示

  7. string使用[[…]]表示字符串。这种形式的字符串可以包含多行也,可以嵌套且不会解释转义序列,如果第一个字符是换行符会被自动忽略掉。这种形式的字符串用来包含一段代码是非常方便的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    page = [[
    <HTML>
    <HEAD>
    <TITLE>An HTML Page</TITLE>
    </HEAD>
    <BODY>
    Lua
    [[a text between double brackets]]
    </BODY>
    </HTML>
    ]]

    io.write(page)
  8. Lua会自动在string和numbers之间自动进行类型转换,当一个字符串使用算术操作符时,string就会被转成数字。

    1
    2
    3
    4
    print("10" + 1)             --> 11
    print("10 + 1") --> 10 + 1
    print("-5.3e - 10" * "2") --> -1.06e-09
    print("hello" + 1) -- ERROR (cannot convert "hello")

    反过来,当Lua期望一个string而碰到数字时,会将数字转成string。

    1
    2
    print(10 .. 20)      --> 1020
    print(type(10 .. 20)) --> string

    ..在Lua中是字符串连接符,当在一个数字后面写时,必须加上空格以防止被解释错。

    管字符串和数字可以自动转换,但两者是不同的,像10 == “10”这样的比较永远都是错的。如果需要显式将string转成数字可以使用函数tonumber(),如果string不是正确的数字该函数将返回nil。

    1
    2
    3
    4
    5
    6
    7
    line = io.read()         -- read a line
    n = tonumber(line) -- try to convert it to a number
    if n == nil then
    error(line .. " is not a valid number")
    else
    print(n*2)
    end

    反之,tostring()将数字转成字符串,这种转换一直有效

    1
    2
    print(tostring(10) == "10")     --> true
    print(10 .. "" == "10") --> true
  9. Lua5.1中,字符串前放置操作符”#“来获得该字符串的长度

    1
    2
    3
    a = "hello"
    print(#a) -->5
    print(#"good\0bye") -->8

table (表)

table是Lua的主要的数据结构,具有强大的功能。

  1. table无固定的大小,可以动态增长

  2. 就Lua的习惯而言,如果以数字作为table的索引值的话,一般从1开始

  3. table实现了“关联数组”:可以通过整数或者任意其他类型的值(除了nil)来索引

  4. Lua中无法声明table。创建table是通过构造表达式进行创建的。最常用的是{}

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    a = {}  -- 创建一个table,并将它的引用存储到a上
    k = "x"
    a[k] = 10
    a[20] = "great"
    print(a["x"]) -->10

    y = 20
    print(a[y]) --> great

    a["x"] = a["x"]+1
    print(a["x"]) -->11
  5. table永远是“匿名的”,一个持有table的变量与table自身没有任何固定的关联

    当一个程序再也没有对一个table的引用时吗,Lua的垃圾收集器(GC – garbage collection)最终会删除该table,并且复用它的内存

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    a = {}  -- 创建一个table,并将它的引用存储到a上
    k = "x"
    a[k] = 10
    print(a["x"]) -->10

    b = a
    print(b["x"]) -->10

    a = nil -- 删除该变量
    print(b["x"]) -->10

    b = nil --删除完本程序对table的引用
  6. table索引的语法糖

    1
    2
    3
    4
    5
    6
    7
    a = {}
    x = "y"
    a[x] = 10

    print(a[x]) --> 10
    print(a.x) --> nil -->以字符串x为索引 表示 a["x"]
    print(a.y) --> 10 -->以字符串y为索引 表示 a["y"]
  7. Lua 5.1中长度操作符 # 与table的一些用法

    Lua 5.1中“#”长度操作符可以返回一个数组或者线性表的最后一个索引值(或为其大小)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    a = {}
    for i=1,5 do
    a[i] = io.read() -->读取一行
    end

    -- 打印所有读取的内容
    for i=1,#a do
    print(a[i])
    end

    ---
    print(a[#a]) --打印列表a的最后一个值
    a[#a] = nil --删除最后一个值
    a[#a+1] = 11 --将一个新的值添加到a的末尾

    -- 读取一个文件的前十行
    a = {}
    for i=1,10 do
    a[#a+1] = io.read()
    end
  8. Lua将nil作为界定数组结尾的标志

    当一个数组中间有nil时,使用#a得到的就不会是你的预期

    可以使用table.maxn(a)函数 – (Lua5.1新函数),来取得table的最大索引数

function (函数)

Lua中的函数可以存储在变量之中,

可以通过参数传递给其他函数,

也可以作为其他函数的返回值。

userdata (自定义类型)

userdata可将任意的C语言数据存储到Lua变量之中。

userdata用于表示一种由应用程序或C语言库所创建的新类型,例如标准的I/O库就用userdata来表示文件.

thread (线程)

表达式

算数运算符

加 + 减 - 乘 * 除 / 指数 ^ 取模 % 负号 -

和C语言的几乎相同,不过Lua中的数据都是双精度浮点数

  • Lua中没有 +=, ++ ,–这一类的运算符

但是对于这些实数而言有些其他的不同

  • x %1 得到x的小数部分
  • x - x%1 得到x的整数部分
  • x - x%0.01 精确到小数点后两位的结果
1
2
3
4
5
x = math.pi
print(x) -->3.1415926535898
print(x%1) -->0.14159265358979
print(x-x%1) -->3
print(x-x%0.01)-->3.14

关系操作符

1
2
<	>	<=	>=	== ~=
不等于

运算结果都是true或者false

  • nil只与其自身相等

  • 对于table,userdata和function,Lua是做引用比较的,即只有他们引用同一个对象是才相等

  • 比较字符串是按照字母次序进行比较的,与字符串长度无关

  • Lua遇到字符串和数字做比较会引发错误

    1
    2
    3
    print(5 < 10)     -->true
    print("a" < "b") -->true
    print("ab" < "b") -->true

    逻辑操作符

and or not

  • and和or的运算结果不是true和false,而是和它的两个操作数相关。

    1
    2
    3
    a and b    -- 如果a为false,则返回a,否则返回b

    a or b -- 如果a为true,则返回a,否则返回b
  • 注意and和or都是短路求值,只有在需要的时候才会去评估它的第二个操作数

    1
    2
    3
    4
    5
    6
    -- 一个很实用的技巧:如果x为false或者nil则给x赋初始值v
    x = x or v
    -- 等价于
    if not x then
    x = v
    end
  • c语言的三目运算符 a ? b : c

    1
    2
    (a and b) or c

    • 求x y中较大的数
      max = ( x > y) and x or y
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
        

      ### 操作符优先级

      从高到低

      ​```lua
      ^
      not - (负号)
      * /
      + -
      ..
      < > <= >= ~= ==
      and
      or

      table的构造

构造器是创建和初始化表的表达式。表是Lua特有的功能强大的东西。最简单的构造函数是{},用来创建一个空表。可以直接初始化数组:

1
2
3
days = {"Sunday", "Monday", "Tuesday",  "Wednesday",

"Thursday", "Friday", "Saturday"}

Lua将”Sunday”初始化days[1](第一个元素索引为1),用”Monday”初始化days[2]

构造函数可以使用任何表达式初始化:

1
2
tab  = {sin(1), sin(2), sin(3), sin(4),
sin(5),sin(6), sin(7), sin(8)}

如果想初始化一个表作为record使用可以这样:

1
2
3
4
5
6
a = {x=0, y=0}    <-->    a = {}; a.x=0; a.y=0  

a = {x=10,y=20}
print(a[1]) --> nil
print(a["x"]) --> 10
print(a.x) --> 10

不管用何种方式创建table,我们都可以向表中添加或者删除任何类型的域,构造函数仅仅影响表的初始化。

1
2
3
4
5
6
7
8
9
10
w = {x=0, y=0, label="console"}
x = {sin(0), sin(1), sin(2)}

w[1] = "another field" -- 添加key 1到table w中,值为 another field
x.f = w -- 添加key "f"到table x中,值为 w

print(w["x"]) --> 0
print(w[1]) --> another field
print(x.f[1]) --> another field
w.x = nil -- remove field "x"

每次调用构造函数,Lua都会创建一个新的table,可以使用table构造一个list:

1
2
3
4
list = nil
for line in io.lines() do
list = {next=list, value=line}
end

这段代码从标准输入读进每行,然后反序形成链表。下面的代码打印链表的内容:

1
2
3
4
5
l = list
while l do
print(l.value)
l = l.next
end

在同一个构造函数中可以混合列表风格和record风格进行初始化,如:

1
2
3
4
5
6
polyline = {color="blue",  thickness=2, npoints=4,
{x=0, y=0},
{x=-10, y=0},
{x=-10, y=1},
{x=0, y=1}
}

这个例子也表明我们可以嵌套构造函数来表示复杂的数据结构.

1
2
3
print(polyline["color"])  --> blue
print(polyline[1].x) --> 0
print(polyline[2].x) --> -10

上面两种构造函数的初始化方式还有限制,比如你不能使用负索引初始化一个表中元素,字符串索引也不能被恰当的表示。下面介绍一种更一般的初始化方式,我们用[expression]显示的表示将被初始化的索引:

1
2
3
4
5
6
7
8
opnames = {["+"] = "add", ["-"] = "sub",
["*"] = "mul", ["/"] = "div"}

i = 20; s = "-"
a = {[i+0] = s, [i+1] = s..s, [i+2] = s..s..s}

print(opnames[s]) --> sub
print(a[22]) --> ---

list风格初始化和record风格初始化是这种一般初始化的特例:

1
2
3
4
5
{x=0, y=0}    <-->    {["x"]=0, ["y"]=0}

{"red", "green", "blue"} <-->

{[1]="red", [2]="green", [3]="blue"}

如果真的想要数组下标从0开始:

1
2
days = {[0]="Sunday", "Monday", "Tuesday", "Wednesday",
"Thursday", "Friday", "Saturday"}

注意:不推荐数组下标从0开始,否则很多标准库不能使用。

在构造函数的最后的”,”是可选的,可以方便以后的扩展。

1
a =  {[1]="red", [2]="green", [3]="blue",}

在构造函数中域分隔符逗号(”,”)可以用分号(”;”)替代,通常我们使用分号用来分割不同类型的表元素。

1
{x=10, y=45; "one", "two", "three"}

语句

赋值

大体和C语言类似,不过没有+=,-=这一类的赋值。

Lua允许多重赋值,即对多个变量同时赋值。变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。

1
a, b = 10, 2*x       <-->       a=10; b=2*x

遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值

1
2
x, y = y, x                     -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i] -- swap 'a[i]' for 'a[i]'

局部变量

使用local创建一个局部变量,与全局变量不同,局部变量只在被声明的那个代码块内有效。

需要注意的是:lua中的变量如果没有特殊说明,全是全局变量,那怕是语句块或是函数里。变量前加local关键字的是局部变量。

尽可能的去使用局部变量。

  • 访问局部变量比访问全局变量要快
  • 局部变量会随着其作用域的消失而结束,便于垃圾收集器释放其值。

if then else语句

控制结构的条件表达式结果可以是任何值,Lua认为false和nil为假,其他值为真

  • Lua不支持switch语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
-- 第一种
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语句

1
2
3
while condition do
statements;
end;

repeat until语句

1
2
3
repeat
statements;
until conditions;

for 语句 数字型

1
2
3
for var=exp1,exp2,exp3 do
loop-part
end

for将用作为从exp1(初始值)到exp2(终止值),按照步长exp3执行。其中步长可以省略,默认为1.

  • 三个表达式只会被计算一次,并且是在循环开始前。
  • 控制变量var是局部变量自动被声明,并且只在循环内有效.
  • 循环过程中不要改变控制变量的值,那样做的结果是不可预知的。如果要退出循环,使用break语句。

for 语句 范型

1
2
-- 范型for遍历迭代子函数返回的每一个值。
for i,v in ipairs(a) do print(v) end

Lua的基础库提供了ipairs,这是一个用于遍历数组的迭代器函数。在每次循环中,i会被赋予一个索引值,同时v被赋予一个对应于索引值的数组元素值。

  • 控制变量是局部变量
  • 不要修改控制变量的值

break 语句

break语句用来退出当前循环(for、repeat、while),在循环外部不可以使用。它只会跳出内部循环。

return 语句

return用来从函数返回结果,或者结束一个函数的执行。当一个函数自然结束时,结尾会有一个默认的return。

Lua语法要求break和return只能出现在block的结尾一句(也就是说:作为chunk的最后一句,或者在end之前,或者else前,或者until前),例如:

1
2
3
4
5
local i = 1
while a[i] do
if a[i] == v then break end
i = i + 1
end