130:小试Lua(1)

130:小试Lua(1)

Lua 是一门小型的脚本语言。我学习他的最初目的是因为他简单的设计理念,简单的语法和实现,学习成本很低(但是要学好还是难的),以及由于其实现简单,被广泛运用在嵌入式领域中,我觉的很有意思。后面在慢慢学习的过程中,我发现Lua也是学习编译原理、虚拟机和脚本引擎的一个很好的开始。恰巧我对这些都很感兴趣。所以暑假我想好好研究一下这个语言,看看能不能把他融入进我现在技术路线里面。

我参考的书是《Lua程序设计(第四版)》和菜鸟教程,之后可能会参考一些新的吧但是不重要,我使用的版本是Lua5.3。今天的学习目标主要是掌握Lua的基本语法和数据结构,了解一下这个语言的框架,那么现在就开始吧。学习顺序就按书上来了。

语言基础

Lua语言入门

简单的规范

关于变量的命名要求和风格和C差不多,不能以数字开头,通常为任意字母、数字、下划线组成。其中_ + 大写字母的用法一般有特殊用途,_ + 小写字母的用法为哑变量(就是0/1的变量)

保留字很少,这里记录一下就当是熟悉了:

and    break    do       else    elseif
end    false    goto     for     function
if     in       local    nil     not
or     repeat   return   then    true
until  while

除了repeatuntil其他的还挺熟悉的

然后注释有两种类型:

-- 这是单行注释
print(0)
--[[
这是多行注释
这是多行注释
--]]

语句间的分隔比较自由,我的习惯还是以;分割

全局变量

Lua语言中,变量默认为全局变量,使用未初始化的全局变量会得到nil,不会得到错误:

> a
nil
> a = 1
> a
1

同样的,将nil赋值给变量,就等同于回收这个变量:

> a = nil
> a
nil

类型和值

Lua是动态类型语言,每个值自带类型信息,Lua中以下八种基本类型:

  • nil(空)
  • boolean(布尔)
  • number(数值)
  • string(字符串)
  • userdata(用户数据)
  • thread(线程)
  • function(函数)
  • table(表)

我们可以通过type()来查看值的对应的类型:

> type(nil)
nil
> type(type)
function
> type(type(type))
string
> type(3.14)
number
> type({})
table
> type(io.stdin)
userdata

这里的userdata和C相关,关于他的解释,我现在有点看不懂,所以就不做解释了。

nil则是一个特殊的类型,他的作用就是和其他所有值进行区分。它代表无效值。

Boolean是布尔类型,除了falsenil以外的所有值均为真,这里的逻辑运算符运算结果为:

  • and:若第一个数为假,则返回第一个数
  • or:若第一个数为真,则返回第一个数

由于关键词和逻辑运算符比较少,所以这一部分的设计会有点复杂,后面应该用多了就还好了。

参数传递

编译器在运行代码之前会创建一个叫arg的表,用来存储命令行参数:

  • 参数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"

Lua中也有可变长参数,不过等后面再说。

数值

在Lua5.3开始,Lua的数值格式只有两种选择:

  • 被称为integer的64位整型
  • 被称为float的双精度浮点数(不是C中的单精度)

数值常量

在Lua中我们除了正常书写一个整数或者小数,我们还可以通过科学计数法来表示一个数值:

> 0xff	-- 支持十六进制
255
> 3.4
3.4
> 3e2
300.0
> 3e-2
0.03
> 5E20
5e+20

其中,我们把具有十进制小数指数的数值当作浮点数值,否则则视为整数。我们可以通过math.type()来分辨number是整数还是浮点数:

> math.type(1)
integer
> math.type(1.0)
float
> math.type(1e+10)
float

由于integerfloat都是number类型,所以他们可以相互转换。具有相同算术值的整型值和浮点值在Lua中是相等的:

> 1 == 1.0
true
> -2 == -2.000
true
> 1e2 == 100
true

算数运算

和其他语言差不多,主要遵循以下规则:

  • 如果两个操作数都是整型,那结果一定是整型
  • 反之,结果是浮点数。当一个操作数是浮点数时,Lua会把另一个操作数也转换成浮点数
  • 由两个整数相除的结果不要一定是整数,所以除法的结果均为浮点数。除非使用整数除法//

这里不做过说明。

关系运算

Lua提供了一下几种关系运算:

< > <= >= == ~=

他们的运算结果都是Boolean

其中==用来做相等性测试,~=是不等性测试。操作数可以是任意两个值,但是如果值的类型不同,则无法比较,是不等的。

数学库

Lua中有标准库math,提供标准的数学函数。这里就记录下书上的几个例子:

随机数发生器

math.random()用于生成伪随机数,有三种调用方式:

  • math.random() -> 返回一个[0,1)[0,1) 的数
  • math.random(n)-> 返回一个[1,n)[1,n)的数
  • math.random(l,y)-> 返回一个[l,u)[l,u)的数

我们还可以用math.randomseed()设置随机数的种子,常见用法:

math.randomseed(os.time())

取整函数

共提供了三种取整函数:

  • floor:负无穷取整
  • ceil:向正无穷取整
  • modf:向0取整,返回整数部分和小数部分

下面演示一下基本用法了:

> math.floor(3.3)
3
> math.floor(-3.3)
-4
> math.ceil(3.3)
4
> math.ceil(-3.3)
-3
> math.modf(3.3)
3	0.3
> math.modf(-3.3)
-3	-0.3

表示范围

数值在范围和精度上是用限制的。

标准Lua使用64bit来存储整数,其最大值为26412^{64}-1,我们可以通过数学库的常量math.maxintegermath.minteger来访问整型的最大和最小值:

> math.maxinteger
9223372036854775807 -- 0x7fffffffffffffff
> math.mininteger
-9223372036854775808 -- 0x8000000000000000

大于这个值的数就会溢出然后回环。

浮点数的范围大概是[10308,10308][-10^{308},10^{308}],这里我就不过多说明

有个小技巧是:可以通过对整型加上0.0从而将整型转换成浮点数

剩下的就是一些,边界情况,这里就不做讨论

运算符优先级

这个和C是相同的,不做说明

字符串

在Lua中字符串既可以是单个字符也可以是一段长文本,并没有直接的限制。同时单字符由8位存储,对于特殊的编码方式,如UTF-8会在后面提到。

需要注意的是Lua的字符串是不可变的值。我们不能像C一样对字符串中的单个词进行修改,我们需要创建一个新的字符串以达到改变字符串内容的效果。(Lua中的字符串是自动内存管理的对象,所以不用手动回收)

> a = "one string"
> b = string.gsub(a, "one", "another")
> a
one string
> b
another string

我们可以使用#作为长度操作符,获取字符串的长度:

> #b
14
> #a
10
> #"hello world"
11

我们还可以通过连接字符串..来进行字符串的拼接(拼接时会把数转换为字符串形式再拼接):

> a = "hello"
> a .. "world"
helloworld
> a .. " world"
hello world
> a
hello

字符串常量

a = "hello"b = 'world'这几种形式的声明就是字符串常量,所以这里不用做过多说明,其中字符串常量的转义字符和C是差不多的所以也不必多说。

需要提一嘴的是,在Lua中可以通过转义序列\u{h..h}的方式来声明UTF-8字符常量:

> "\u{3b1} \u{3b2} \u{3b3}"
α β γ

长字符串/多行字符串

Lua中的特殊语法——可以使用一对双方括号来声明长字符串/多行字符串,下面就是一个很好的例子:

page = [[
<html>
<head>
    <title> Hello </title>
</head>
<body>
    <a href="http://www.lua.org"> Lua </a>
</body>
</html>
]]
print(page)

得到:

> lua hello.lua
<html>
<head>
    <title> Hello </title>
</head>
<body>
    <a href="http://www.lua.org"> Lua </a>
</body>
</html>

这里可以注意到[[后的第一个换行符是不生效的,但是在]]前的依然生效,需要注意一下。

强制类型转换

Lua在运行时提供了数值和字符串的自动转换。针对字符串的所有算数操作都会转换成数值进行,以及在参数应该是数值时进行。相反的,当解释器发现在需要字符串的地方出现了数值,也会将数值转换成字符串。

如果需要显示的将一个字符串转成数值,我们可以使用这个函数tonumber()

> tonumber("-3")
-3
> tonumber("3123")
3123
> tonumber("3123.0")
3123.0
> tonumber("asd")
nil
> tonumber("10e4")
100000.0

我们还可以给tonumber()提供第二个参数来指明进制转换:

> tonumber("11111111",2)
255
> tonumber("ff",16)
255
> tonumber("123",4)
27
> tonumber("123",3)
nil --传入的进制参数无法正确转换,所以为nil

相反的,调用函数tostring()也可以将数值转换成字符串:

> tostring(10) == "10"
true

字符串标准库

Lua处理字符串的完整能力,主要来自于标准字符串库,这里就按照书上的顺序一点点复现一遍

  • string.len(s)-> 获取s的字符串长度,等价于#s
  • string.lower()/string.upper-> 将所有字符小写/大写
  • string.rep(s,n)-> 将字符串s重复n次
  • string.reverse(s)-> 将字符串反转

e.g.

> a = "Hello World"
> string.len(a) == #a
true
> string.upper(a)
HELLO WORLD
> string.lower(a)
hello world
> string.rep(a,3)
Hello WorldHello WorldHello World
> string.reverse(a)
dlroW olleH
  • string.sub(s,i,j)-> 从字符串s中提取第i到j个字符

e.g.

> s = "[hello world]"
> string.sub(s,2,-2)
hello world
> string.sub(s,1,1)
[
> string.sub(s,-1,-1)
]
  • string.char(...)-> 接收一个或多个整数作为参数,然后将每个整数转换成对应的字符
  • string.byte(s,i,j)-> 根据参数的数量决定用,总之是将字符转换成整数

e.g.

> string.char(97)
a
> i = 98
> string.char(i,i+1,i+2)
bcd
> string.byte("abc")
97
> string.byte("abc",2)
98
> string.byte("abc",-1)
99
> string.byte("abcdef",1,-1)
97	98	99	100	101	102
> string.byte("abcdef",1,3)
97	98	99
  • string.format(...)-> 和C差不多,用于格式化字符串参数

e.g.

> string.format("x = %d",10)
x = 10
> string.format("x = 0x%x",255)
x = 0xff
> string.format("x = %f",10)
x = 10.000000
> string.format("PI = %.2f",math.pi)
PI = 3.14
> tag,title = "h1","a little"
> string.format("<%s>%s</%s>",tag,title,tag)
<h1>a little</h1>

  • string.gsub(s,a1,a2)-> 将字符串s中的a1替换成a2

e.g.

> string.gsub(s, "l", ".")
He..o Wor.d	3
> string.gsub(s, "ll", ".")
He.o World	1
> string.gsub(s, "a", ".")
Hello World	0

UTF-8编码

我觉的这一部分比较复杂,所以就直接跳过了