在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
官网 http://www.lua.org Lua语言从一开始就被设计为能与C/C++及其他常用语言开发的软件集成在一起使用的语言,这种设计带来了非常多的好处。一方面,Lua语言不需要在性能、与三方软件交互等C语言已经非常完善的方面重复“造轮子”,可以直接依赖C语言实现上述特性,因而Lua语言非常精简;另一方面,通过引入安全的运行时环境、自动内存管理、良好的字符串处理能力和可变长的多种数据类型,Lua语言弥补了C语言在非面向硬件的高级抽象能力、动态数据结构、鲁棒性、调试能力等方面的不足。 Lua语言强大的原因之一就在于它的标准库,这不是偶然,毕竟扩展性本身就是Lua语言的主要能力之一。Lua语言中的许多特性为扩展性的实现提供了支持:动态类型使得一定程度的多态成为了可能,自动内存管理简化了接口的实现(无须关心内存的分配/释放及处理溢出),作为第一类值的函数支持高度的泛化,从而使得函数更加通用。 Lua语言除了是一门可扩展的语言外,还是一门胶水语言(glue language)。Lua语言支持组件化的软件开发方式,通过整合已有的高级组件构建新的应用。这些组件通常是通过C/C++等编译型强类型语言编写的,Lua语言充当了整合和连接这些组件的角色 保留关键字以下列出了 Lua 的保留关键字。保留关键字不能作为常量或变量或其他用户自定义标示符:
一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。大小写敏感 注释两个连续的连字符(--)表示单行注释的开始(从--之后直到此行结束都是注释),使用两个连续的连字符加两对连续左方括号表示长注释或多行注释的开始(直到两个连续的右括号为止,中间都是注释) ---[[ 长注释 --]] 变量全局变量(Global Variable)无须声明即可使用,使用未经初始化的全局变量也不会导致错误。当使用未经初始化的全局变量时,得到的结果是nil
Lua语言中的变量在默认情况下是全局变量,所有的局部变量在使用前必须声明。与全局变量不同,局部变量的生效范围仅限于声明它的代码块。 尽可能地使用局部变量是一种良好的编程风格。 类型有8种基本类型:nil(空)、boolean(布尔)、number(数值)、string(字符串)、userdata(用户数据)、function(函数)、thread(线程)和table(表)
在Lua 5.2及之前的版本中,所有的数值都以双精度浮点格式表示。从Lua 5.3版本开始,Lua语言为数值格式提供了两种选择:被称为integer的64位整型和被称为float的双精度浮点类型(注意,在本书中“float”不代表单精度类型)。对于资源受限的平台,我们可以将Lua 5.3编译为精简Lua(Small Lua)模式,在该模式中使用32位整型和单精度浮点类型。 字符串Lua语言中的字符串是不可变值(immutable value),字符串是一串字节组成的序列. 可以使用长度操作符(length operator)(#)获取字符串的长度。 可以使用一对双引号或单引号来声明字符串常量(literal string)
像长注释/多行注释一样,可以使用一对双方括号来声明长字符串/多行字符串常量。被方括号括起来的内容可以包括很多行,并且内容中的转义序列不会被转义。此外,如果多行字符串中的第一个字符是换行符,那么这个换行符会被忽略。可以在两个左方括号之间加上任意数量的等号,如[===[。这样,字符串常量只有在遇到了包含相同数量等号的两个右方括号时才会结束(就前例而言,即]===])
字符串连接 .. 字符串标准库默认处理的是8 bit(1 byte)字符。这对于某些编码方式(例如ASCII或ISO-8859-1)适用,但对所有的Unicode编码来说都不适用。 函数string.format是用于进行字符串格式化和将数值输出为字符串的强大工具,该函数会返回第一个参数(也就是所谓的格式化字符串(format string))的副本,其中的每一个指示符(directive)都会被替换为使用对应格式进行格式化后的对应参数。 utf8标准库。函数utf8.len返回指定字符串中UTF-8字符(代码点)的个数 表表(Table)是Lua语言中最主要(事实上也是唯一的)和强大的数据结构。使用表,Lua语言可以以一种简单、统一且高效的方式表示数组、集合、记录和其他很多数据结构。Lua语言也使用表来表示包(package)和其他对象。当调用函数math.sin时,我们可能认为是“调用了math库中函数sin”;而对于Lua语言来说,其实际含义是“以字符串"sin"为键检索表math”。 a = {} 创建一个表 a.name等价于a["name"] 当被用作表索引时,任何能够被转换为整型的浮点数都会被转换成整型数。例如,当执行表达式a[2.0]=10时,键2.0会被转换为2。相反,不能被转换为整型数的浮点数则不会发生上述的类型转换
表构造器(Table Constructor)是用来创建和初始化表的表达式,也是Lua语言中独有的也是最有用、最灵活的机制之一。正如我们此前已经提到的,最简单的构造器是空构造器{}。表构造器也可以被用来初始化列表,例如,下例中使用字符串"Sunday"初始化了days[1](构造器第一个元素的索引是1而不是0) days={“MON”, “ TUE“} 数组(array)或列表(list)如果想表示常见的数组(array)或列表(list),那么只需要使用整型作为索引的表即可。同时,也不需要预先声明表的大小,只需要直接初始化我们需要的元素即可: 在Lua语言中,数组索引按照惯例是从1开始的 获取序列长度的操作符# in pairs遍历过程中元素的出现顺序可能是随机的 in ipairs迭代器 遍历是按照顺序进行的 函数Lua语言中一种与众不同但又非常有用的特性是允许一个函数返回多个结果(Multiple Results) 参数列表中的三个点(...)表示该函数的参数是可变长的。 函数table.unpack与函数table.pack的功能相反。pack把参数列表转换成Lua语言中一个真实的列表(一个表),而unpack则把Lua语言中的真实的列表(一个表)转换成一组返回值,进而可以作为另一个函数的参数被使用
Lua语言也为面向对象风格的调用(object-oriented call)提供了一种特殊的语法,即冒号操作符。形如o:foo(x)的表达式意为调用对象o的foo方法。 关系运算< > >= <= ==用于相等性测试,~=用于不等性测试 输入输出由于Lua语言强调可移植性和嵌入性,所以Lua语言本身并没有提供太多与外部交互的机制。在真实的Lua程序中,从图形、数据库到网络的访问等大多数I/O操作,要么由宿主程序实现,要么通过不包括在发行版中的外部库实现。单就Lua语言而言,只提供了ISO C语言标准支持的功能,即基本的文件操作等 调用io.read("l")会返回当前输入流的下一行,不包括换行符在内;调用io.read("L")与之类似,但会保留换行符(如果文件中存在)。当到达文件末尾时,由于已经没有内容可以返回,该函数会返回nil。选项"l"是函数read的默认参数。 函数io.open来打开一个文件,该函数仿造了C语言中的函数fopen。这个函数有两个参数,一个参数是待打开文件的文件名,另一个参数是一个模式(mode)字符串。
函数os.execute用于运行系统命令,它等价于C语言中的函数system。 io.popen。[4]同函数os.execute一样,该函数运行一条系统命令,但该函数还可以重定向命令的输入/输出,从而使得程序可以向命令中写入或从命令的输出中读取。 控制结构Lua语言提供了一组精简且常用的控制结构(control structure),包括用于条件执行的if以及用于循环的while、repeat和for。所有的控制结构语法上都有一个显式的终结符:end用于终结if、for及while结构,until用于终结repeat结构。 控制结构的条件表达式(condition expression)的结果可以是任何值。请记住,Lua语言将所有不是false和nil的值当作真(特别地,Lua语言将0和空字符串也当作真)。 时间日期函数os.time 函数os.date 函数os.clock通常具有比秒更高的精度,因此其返回值为一个浮点数。具体的精度与平台相关,在POSIX系统中通常是1毫秒。 编译Lua语言会在运行源代码之前先对其进行预编译。Lua语言也允许我们以预编译的形式分发代码。生成预编译文件(也被称为二进制文件,binary chunk)的最简单的方式是,使用标准发行版中附带的luac程序。 预编译形式的代码不一定比源代码更小,但是却加载得更快。预编译形式的代码的另一个好处是,可以避免由于意外而修改源码。然而,与源代码不同,蓄意损坏或构造的二进制代码可能会让Lua解析器崩溃或甚至执行用户提供的机器码。 模块和包Lua语言从5.1版本开始为模块和包(package,模块的集合)定义了一系列的规则。 当函数的参数只有一个字符串常量时括号是可以省略的,而且一般在使用require时按照惯例也会省略括号 local m = require(“math”) local m = require “math” 面向对象(Object-Oriented)编程使用参数self是所有面向对象语言的核心点。大多数面向对象语言都向程序员隐藏了这个机制,从而使得程序员不必显式地声明这个参数(虽然程序员仍然可以在方法内使用self或者this)。Lua语言同样可以使用冒号操作符(colon operator)隐藏该参数。使用冒号操作符,我们可以将上例重写为a2:withdraw(260.00) 冒号的作用是在一个方法调用中增加一个额外的实参,或在方法的定义中增加一个额外的隐藏形参。冒号只是一种语法机制,虽然很便利,但没有引入任何新的东西。我们可以使用点分语法来定义一个函数,然后用冒号语法调用它,反之亦然
环境变量Lua语言将全局环境自身[1]保存在全局变量_G中(因此,_G._G与_G等价)。例如,如下代码输出了全局环境中所有全局变量的名称:for n in pairs(_G)do print(n)end _G _ENV 协程(Coroutine)协程是一系列的可执行语句,拥有自己的栈、局部变量和指令指针,同时协程又与其他协程共享了全局变量和其他几乎一切资源。线程与协程的主要区別在于,一个多线程程序可以并行运行多个线程,而协程却需要彼此协作地运行,即在任意指定的时刻只能有一个协程运行,且只有当正在运行的协程显式地要求被挂起(suspend)时其执行才会暂停。 Lua语言中协程相关的所有函数都被放在表coroutine中。 函数create用于创建新协程,该函数只有一个参数,即协程要执行的代码的函数(协程体(body))。函数create返回一个"thread"类型的值,即新协程 local co= coroutine.create(function () print(“hello”) end) print(coroutine.status(co)) 当一个协程被创建时,它处于挂起状态,即协程不会在被创建时自动运行。 协程的真正强大之处在于函数yield,该函数可以让一个运行中的协程挂起自己,然后在后续恢复运行。 虽然协程的概念很容易理解,但涉及的细节其实很多。因此,对于那些已经对协程有一定了解的读者来说,有必要在进行进一步学习前先理清一些细节。Lua语言提供的是所谓的非对称协程(asymmetric coroutine),也就是说需要两个函数来控制协程的执行,一个用于挂起协程的执行,另一个用于恢复协程的执行。而其他一些语言提供的是对称协程(symmetric coroutine),只提供一个函数用于在一个协程和另一个协程之间切换控制权。 Lua JITLua JIT是Lua语言的即时(JIT:Just-In-Time)编译器,它提供基于快速解释器和跟踪编译器的虚拟机,可显著提高Lua程序的性能。 Luajit将原生Lua进行了扩展,使它支持JIT方式编译运行,比起原生Lua程序,它有着如下特点: JIT即时编译器让执行效率更高。 它同时兼容传统的AOT编译。 全新设计的Luajit字节码文件格式,更加高效与更强的调试支持。(这一点在后面会着重介绍) 全新的Lua指令集。引入了中间表示IR,以及编译引擎支持不同平台的处理器指令即时编译,完全的符合现代化编译器设计,是编译理论学习的绝佳好资料。
|
请发表评论