lua
简介
lua 是一种轻量级的、嵌入式的、解释型的脚本语言,主要设计目标是嵌入到其他应用程序中作为扩展或脚本语言。它是一个动态类型和垃圾回收语言,通常用于处理数据、脚本化游戏逻辑、配置管理等。
比如某些鼠标就可以通过 lua 来编写宏,自定义一些快捷键啥的。
为啥要学习 lua,每个人都有自己的理由。
对于我来说,仅仅是在人群中多看了它一眼~~~
环境搭建
我所使用的系统是 Ubuntu 22,可以直接通过apt
包管理安装 lua。
- 安装
1 |
|
- 运行
lua 脚本通过 lua 解释器运行,和 Python 差不多。
1 |
|
基础内容
注释
lua 的注释关键字是--
。
1 |
|
输出
lua 和 Python 一样通过print
函数输出内容
1 |
|
变量
- 默认定义为全局变量
在 lua 中直接定义变量默认会定义为全局变量。
1 |
|
- 定义局部变量
lua 通过local
关键字定义局部变量。
1 |
|
方法
- 定义方法
在下例中function
是定义方法的关键字,end
关键字标记方法体的结束。
自然function
和end
关键字之间就是方法的主体功能部分了。
demo
表示方法名,这是我们可以自定义的。
1 |
|
- 传参
lua 的传参和其它编程语言都差不多。
在下例中方法功能为打印传入的name
参数。
1 |
|
控制结构
条件
以下例子就是一个条件判断语句,其中if a > b
就是条件判断部分,它的结果是布尔值(true
或 false
)。
then
关键字用于表示条件成立时要执行的代码块的开始,else
及以后部分自然就是条件不成立时要执行的代码。
最后和定义方法一样,使用end
关键字表示条件判断语句的结束。
1 |
|
循环
在下例中,for
是定义循环的关键字,var = 1, 100
是控制条件,它将变量var
的初始值设置为1
,并指定循环到var = 100
结束。每次循环时,var
会按默认步长自增。
do
关键字表示循环体的开始,循环体中的代码将在每次循环时执行,直到遇到end
关键字循环结束。
1 |
|
数据结构
表
在 lua 中,表是最基本且功能最强大的数据结构。表用于实现数组、字典(哈希表)、对象等多种数据结构。
- 创建表
1 |
|
数组
数组是一个特殊类型的表,它的键通常是整数。在 lua 中,数组的行为与普通表一样,只是它使用整数作为键来访问元素。
并且在 lua 中,数组的索引从 1
开始,而不是从 0
开始,感觉这比较反人类。。。
- 创建数组
1 |
|
- 遍历数组
下面例子中的ipairs
函数专用于遍历具有连续整数索引的数组,它返回数组的索引和元素。
1 |
|
哈希表
哈希表是通过键(可以是任何类型,除了 nil
)来存储数据的一种表。lua 表通过哈希机制来实现键值对的存储,通常用于实现字典。
- 创建哈希表
1 |
|
在哈希表中,键可以是任意类型的值,包括字符串、数字、布尔值等。但是唯独不能是
nil
,nil
在 lua 中表示空,类似于 C 语言中的NULL
。
- 遍历哈希表
下面例子中的pairs
函数用于遍历哈希表,它返回每一对键值对。
1 |
|
数组与哈希表的区别:
- 数组:键是连续的整数,并且是有序的。
- 哈希表:键可以是任意类型的值,通常没有顺序。
API 操作
lua 提供了一套丰富的 API 用于操作表、字符串、文件、输入输出等。通过这些 API,我们可以高效地进行开发。
以下是一些常见的 lua API 操作分类及相关示例:
全局函数
这些函数是 lua 的基础功能,可以在任何地方调用。
type
返回变量的类型。
1 |
|
tostring
将值转换为字符串。
1 |
|
tonumber
将字符串转换为数字。
1 |
|
输入输出
io.read
从标准输入读取数据。
1 |
|
io.write
将数据写入标准输出。
1 |
|
字符串操作
string.len
返回字符串的长度。
1 |
|
string.sub
截取字符串的子串。
1 |
|
string.upper
和string.lower
将字符串转换为大写或小写。
1 |
|
string.find
查找子字符串的位置。
1 |
|
表操作
table.insert
向表中插入一个元素,默认添加到末尾。
1 |
|
table.remove
移除表中的指定元素。
1 |
|
table.concat
将表的元素连接成一个字符串。
1 |
|
table.sort
对表的元素进行排序。
1 |
|
面向对象
创建对象
前面我们提到过,表在 lua 中用于实现对象数据结构,在 lua 中对象是由一个包含方法和属性的表表示的。
首先,我们可以创建一个名为people
的空表,
1 |
|
定义对象方法
这段代码定义了一个匿名函数,并将其分配给 people.sayHi
字段。调用 people.sayHi()
时,Lua 会执行该匿名函数。
通过匿名函数定义sayHi
方法,将它添加为people
表的一个字段。
即该字段的值是一个函数。调用people.sayHi()
这个字段就会调用这个函数的功能。
1 |
|
以下这段代码重新定义了sayHi
方法,使其打印"people say Hi:"
和对象的name
属性。self
参数是 lua 中常见的约定,用来表示方法的调用者(即当前对象)。
1 |
|
复制表来创建实例
clone
函数通过浅拷贝(pairs
遍历)创建了一个新的表ins
,然后将people
表的所有字段(包括方法)复制到新表中。这个新表就是people
类的一个实例。它继承了people
类的方法,可以像调用people
的方法一样调用实例的方法。
1 |
|
这里通过clone(people)
创建了一个新的对象p
,并调用了p.sayHi()
。由于p
是people
的副本,它可以使用 people
中定义的sayHi
方法。
1 |
|
构造方法
在这段代码中,people.new
字段定义了一个构造函数,它创建了一个新的people
实例,并为其设置了name
属性。这个构造函数使用了之前定义的clone
函数来创建一个对象实例,并将name
属性添加到该实例上。
1 |
|
使用 people.new("zhanshan")
创建了一个新的对象 p
,并为其设置了 name
属性为 "zhanshan"
。然后,通过 p:sayHi()
调用sayHi
方法,输出 "people say Hi: zhanshan"
。
1 |
|
继承
这里定义了一个名为Man
的新表,并在其中定义了new
构造函数。Man.new
函数通过调用people.new(name)
来创建 people
类的一个实例,然后返回该实例。这样,Man
类的对象就继承了people
类的所有属性和方法。
1 |
|
多态
通过Man.new("John")
创建了一个Man
类的实例m
,该实例继承了people
类的方法sayHi
。因此,调用m:sayHi()
时,默认执行的是people
类中的sayHi
方法,输出"people say Hi: John"
。
如果Man
类重写了sayHi
方法,那么调用m:sayHi()
时,会执行Man
类中的重写版本,覆盖父类people
中的实现。
如果Man
类没有重写sayHi
方法,则会调用从people
类继承而来的sayHi
,表现为父类的行为。
这种机制体现了多态的特性:相同的接口(方法调用)可以根据对象的实际类型表现出不同的行为。
1 |
|
和 C 联结
lua 可以与 C 语言进行集成和交互,通过调用 C 语言函数或者将 C 语言库嵌入到 lua 脚本中来实现更高效的功能扩展。lua 提供了丰富的 API 来实现 C 和 lua 的交互,主要包括以下两种方式:
- lua 的 C API:通过在 C 语言中使用 lua 提供的 API 来调用 lua 脚本。
- C 函数暴露给 lua:将 C 语言函数暴露给 lua 脚本,这样 lua 脚本就可以调用 C 函数。
将 lua 嵌入到 C 程序中
我们可以将 lua 脚本嵌入到 C 程序中并执行 lua 代码。首先需要在 C 代码中包含 lua 库,并使用 lua 提供的函数来初始化 lua 虚拟机、执行 lua 代码、调用 lua 函数等。
- C 代码
1 |
|
- lua 代码
创建为script.lua
文件。
1 |
|
- 编译
确保已经安装 lua 库。
如果安装库后,编译失败并且显示缺少库就手动指定库文件路径。
1 |
|
- 运行输出
1 |
|
从 lua 调用 C 函数
C 函数可以被暴露给 lua,允许 lua 脚本调用这些 C 函数。通过lua_register
或其他 C API 函数,你可以将 C 函数注册到 lua 环境中,使得 lua 可以像调用普通函数一样调用 C 函数。
- C 代码
1 |
|
- lua 代码
1 |
|
- 编译
1 |
|
- 运行输出
1 |
|
从 C 调用 lua 函数
你也可以在 C 代码中调用 lua 函数。首先需要将 lua 函数加载到栈中,然后通过lua_pcall
执行它。
- C 代码
1 |
|
- lua 代码
创建为c_lua.lua
文件。
1 |
|
- 编译
1 |
|
- 运行输出
1 |
|