首页
开发技巧正文内容

Lua:简单易学且功能齐全的编程语言

2024年08月26日
阅读时长 4 分钟
阅读量 6
Lua:简单易学且功能齐全的编程语言

学习 Lua 确实比学习 Python、Ruby 和 JavaScript 更容易!

创建一个简单易学的解释型编程语言并不难。我们只需要使用任何喜欢的编程语言编写一个解析器和语句运行器,基于简单的语言规范。为了提高性能,我们可以使用基于字节码的执行系统,而不是像 Bash 解释器那样直接执行解析后的源代码。任何人都可以创建简单易学的语言,但并非每种易学的语言都会成为一个有用的通用语言。例如,创建一个只支持基本算术运算的简单脚本语言并不会成为每个人都可以用来编程的有用语言。

大多数开发人员认为 Python、Ruby 和 JavaScript 是易学、简单且有用的语言。这些简单的语言提供了开发人员友好、高效、简单的语法,激励每个程序员使用它们来构建任何软件项目,并借助基于社区的库。毫无疑问,Python 提供了比 JavaScript 更简单的语法 - 对大多数程序员来说,编写 Python 代码感觉就像编写伪代码。Python 是有史以来最简单(但有用)的语言吗?

Lua 是一种动态类型、轻量级、可嵌入、功能齐全的通用语言,比 Python 更容易学习。大多数游戏开发人员了解 Lua 语言,因为它广泛用作嵌入式脚本语言,嵌入在基于 C/C++ 的游戏引擎中。然而,大多数非游戏开发人员不了解 Lua - 他们仍然认为 Python 是世界上最容易的语言。在这篇文章中,我们将探讨 Lua 提供的简单性。


Lua,一门几分钟就能掌握的语言 - 而非几年

编程语言具有不同的学习曲线。您可以更快地学习一些语言,因为它们具有较少的关键字、最小的语法和较少的独特核心概念。与此同时,如果一些语言引入超越传统理论编程概念的独特概念,对于新手来说,这些语言可能变得更加复杂。

Lua 是一种简单的语言,您可以利用您已有的计算机科学知识掌握它 - 您无需学习任何超出核心编程基础的独特内容就能成为 Lua 专家。

Lua 只有 22 个关键字、8 种数据类型和只有一种数据结构,您可以使用它来构建任何复杂的结构。如果您知道如何编写理论伪代码,您就可以在 Lua 中编写计算机程序 - 在纸上编写 Lua 代码就像编写伪代码一样:

function fact(n)
    if n == 1 then 
        return 1
    end
    return n * fact(n - 1)
end

print("fact(3) = " .. fact(3))   -- fact(3) = 6
print("fact(5) = " .. fact(5))   -- fact(5) = 120

看看上面递归阶乘数生成程序的简单性。它没有复杂化源代码的花哨语法 - 语言语法对大多数开发人员来说是不言自明的。Lua 使用 end 关键字定义控制块,类似于经典伪代码。它使用 .. 进行连接,-- 作为单行注释的前缀。

您甚至可以编写一行式的 if 块如下:

function fact(n)
    if n == 1 then return 1 end
    return n * fact(n - 1)
end

Lua 提供了一个比 Python 更简单的数值 for 循环语法:

for i = 1, 10 do
    print(i)
end

在几乎所有情况下,Lua 努力通过保持整体语言简单性来最大程度地重用现有语法,而不是引入新的语法。看看上面的数值 for 循环如何使用赋值运算符。有些语言看起来很简洁,但却有许多隐藏的概念和语法,因此即使开发人员可以在几分钟内开始使用这些语言,他们可能会花费数年时间掌握它们。

您可以在几秒钟内学会 Lua,并在几分钟内掌握它!


一种数据结构用于万物

现代编程语言通常提供多种预先开发的数据结构,如数组、列表、映射、队列、向量、集合等,但在大多数程序中我们只使用少数数据结构。当特定编程语言添加新的数据结构时,可能会为每个结构引入新的语法,从而影响语言的最小设计,例如,Python 有三种初始化三种数据结构的语法:

type([1, 2])                # <class 'list'>
type((2, 5))                # <class 'tuple'>
type({"a": 10, "b": 20})    # <class 'dict'>

Lua 使用一种称为表结构的关联数组结构来处理所有内容。它让用户只需使用基于大括号的一种语法就可以创建数组、映射,以及任何东西:

local array = {1, 4, 10, 12}
array[1] = 10

print(array[1])                 -- 10(Lua 中数组索引从 1 开始)

local map = {width = 200, height = 100}
map["width"] = 250
map.width = 350

print(map.width)                -- 350

print(type(array), type(map))   -- table   table

上面的代码片段使用表创建了 array 数组。当我们不使用关联键值对时,表实例可以作为具有数值索引的传统数组访问。Lua 允许您使用类似 C 结构初始化的赋值运算符创建映射。当您使用表结构创建映射时,您可以使用类似 JavaScript 的属性访问语法,就像上面的 map 变量所示。

上面的代码片段使用 local 关键字使这些变量成为局部变量,因为 Lua 是一种词法作用域简单语言。Lua 设计了有史以来最简单的语法来获取数组的长度:

print(#{1, 2, 5, 1})      -- 4

local arr = {1, 2}
print(#arr)               -- 2

您可以使用 Lua 表实现任何复杂的数据结构,如 此 GitHub 仓库 中所示。使用基本 Lua 表结构开发数据结构可以提高您的数据结构和算法技能。以下故事解释了为什么每个开发人员都应该学习数据结构和算法概念:

为什么每个开发人员都应该学习数据结构和算法


为现代开发人员提供友好、高效的环境

每种最小语言可能并不提供现代开发人员友好、高效的语法和功能。例如,C 无疑是一种只有 32 个关键字的最小语言,但它并不为现代开发人员提供友好、高效的环境,因为它没有映射结构、动态列表、高效的字符串处理方法、自动内存管理(垃圾回收)、面向对象编程 特性、以及以提高生产力为重点的简写功能。

Lua 是一种最小语言,但它经过精心设计,以最简洁的方式回答开发人员在最小化设计方面的每个需求。Lua 允许您使用类似 Python 的现代方法迭代数组和映射:

local vowels = {"a", "e", "i", "o", "u"}
for i, v in ipairs(vowels) do
    print(i, v)
end

print("----")

local scores = {john = 120, david = 80, ann = 120, julia = 52}
for k, v in pairs(scores) do
    print(k, v)
end

上面的 Lua 代码片段使用 ipairs()pairs() 全局可迭代函数打印 vowelsscores 结构的内容,如下所示:

在 Lua 中迭代基于表的结构,作者的屏幕录制

Lua 支持多重赋值和多返回值函数,作为一种友好的现代语言:

local a, b = 10, 20
print(a, b)        -- 10   20

function getsize()
    return 20, 30
end

local w, h = getsize()
print(w, h)        -- 20    30

Lua 是一种多范式语言,因此它为函数式和面向对象风格提供了特性。例如,它允许您创建 lambda 函数如下:

function exec(func)
    print("Running lambda...")
    func()
end

exec(function() print("Lua") end)   -- Lua
exec(function() end)                -- (空函数)

Lua 并不像大多数面向行业的编程语言(如 C# 或 Java)提供许多内置的面向对象编程特性,但它提供了一种类似 Go 的最小类创建,没有内置继承特性:

Rect = {}

function Rect:new(width, height)
    self.width = width
    self.height = height
    return setmetatable({}, {__index = self})
end

function Rect:area()
    return self.width * self.height
end

local rect = Rect:new(100, 50)
print(rect:area())               -- 5000

local square = Rect:new(50, 50)
print(square:area())             -- 2500

在这里,我们使用 Rect 表结构创建了一个类,并通过使用 setmetatable() 内置函数和 __index 元方法在表中附加了类属性和方法。您还可以通过使用原型系统和元表构建继承来实现继承。从官方文档了解更多关于元表的知识。


一个最小但功能强大的标准库Lua拥有一个精简但功能强大的预导入标准库,提供了数学函数、文件处理、操作系统函数、非抢占式多线程、调试、字符串操作、表操作以及与动态链接库通信的功能。Lua的标准库也是多范式的,这意味着你可以通过传递标识符调用标准库函数,或者将它们作为绑定对象的方法来调用。

例如,看看以下Lua代码片段如何调用字符串函数/方法:

local msg = "Lua"

print(string.lower(msg))     -- lua(使用函数式风格)
print(msg:lower())           -- lua(使用面向对象风格)

print(string.reverse(msg))   -- auL
print(string.sub(msg, 1, 2)) -- Lu

借助标准库中的面向对象风格支持,你可以高效地链式调用字符串方法,如下所示:

local msg = "Hello Lua"

print(msg:sub(7):lower():reverse())   -- aul

Lua没有实现正则表达式,因为它会影响Lua嵌入式程序的大小和Lua参考实现的复杂性,因此它提供了一种轻量级的类似正则表达式但最小化的模式匹配实现,如下示例所示:

local productcode = "BL-202 AL-233"

for prefix, num in string.gmatch(productcode, "([A-Z]+)-(%d+)") do
    print(prefix, num) -- BL  202 .. AL 233
end

典型的正则表达式实现需要编写超过4000行的代码,但Lua通过不到500行的代码实现了自己的类似正则表达式但轻量级的模式匹配解决方案:

Lua的类似正则表达式的模式匹配实现,作者的屏幕截图

Lua提供了一种简单的方式来读取标准输入流,因此构建REPL(交互式解释器)程序非常高效,如下示例所示:

local lastname = ""
while 1 do
    io.write("Enter your name: ")
    input = io.read()
    if input == ":exit" then 
        print("Goodbye " .. lastname)
        break 
    end
    
    print(string.format("Hello %s, Welcome", input))
    lastname = input
end

使用Lua io标准库包编写的简单REPL程序,作者的屏幕录制

Lua中的文件操作确实非常高效。看看下面的示例,它读取并打印Lua源文件的内容:

local file = io.open("main.lua", "rb")
print(file:read("*all"))

Lua还通过os模块导出操作系统级操作,并通过package模块提供了调用动态链接库函数的方式。你可以从官方文档中探索所有可用的Lua标准库模块。


一个不会让程序员困惑的错误处理策略

程序员应该正确处理程序中的错误。否则,特定程序可能由于关键错误而停止,或产生无效输出。在现代软件开发行业中,使用基于try-catch的异常是最常用的错误处理策略。如果使用得当,使用try-catch异常是一个不错的策略,但异常经常会使代码库复杂化。由于这个问题,Google C++代码风格指南不建议使用异常,而且Golang也不实现支持基于try-catch的异常。类似C语言的旧式错误码返回方法是简化错误处理需求的方法。

Lua不提供类似Java的基于try-catch的异常,但它提供了类似Go的基于错误码的简化错误处理策略,你可以将其用作基于异常的错误处理方法。

看看下面的示例:

function getresult(score)
    if score > 100 then
        error({code = 1002, msg = "Score shouldn't be higher than 100"})
    elseif score >= 50 then
        return 'P'
    else
        return 'F'
    end
end

for _, v in pairs({20, 120, 60}) do
    local ok, res = pcall(getresult, v)
    if ok then
        print("Result: " .. res)
    else
        print(string.format("Error [%s]: %s", res.code, res.msg))
    end
end

默认情况下,Lua在错误时会停止代码执行,因此如果你的程序尝试对包含字母的两个字符串执行算术运算,程序将抛出错误并停止。pcall()全局函数允许你捕获这些错误,并通过在受保护的执行模式中执行代码来继续代码执行。

上面的代码片段通过在getresult()函数实现中调用error()全局函数来抛出错误。它通过使用pcall()调用getresult()函数来检查错误状态。因此,上面的代码片段在屏幕上打印错误负载并继续执行如下:

演示错误处理示例代码,作者的屏幕录制

使用这种技术,我们可以简单地进行错误处理,而不是像其他流行的现代语言中那样使用冗长的try-catch块。pcall()函数在抛出错误时为第二个参数动态设置错误表 - 否则,它设置典型的返回值。


一个不使用任何特殊关键字的模块系统

如果你使用过JavaScript,你会了解JavaScript模块系统的复杂性。早期,Node.js运行时使用了CommonJs模块系统。ECMAScript(ES)标准引入了一个新标准来创建JavaScript模块,然后Node.js开始支持ES模块。因此,为每个模块系统引入了不同的文件扩展名,即.cjs.mjs.cts等。标准ES模块系统为JavaScript添加了三个新关键字/特殊标识符:exportimportas。同样,大多数流行的编程语言为模块系统保留了专用关键字。

Lua的模块系统只使用主要的return关键字和内置的require()全局函数。Lua的模块不实现任何保留的全局标识符,如CommonJs中的module - 它使用内置的表结构来定义模块,如下示例所示:

-- calc.lua
local calc = {}

function calc.add(a, b)
    return a + b;
end

return calc

上面的代码片段通过添加add()函数在calc.lua文件中定义了一个名为calc的模块。现在,你可以使用require()函数导入和调用模块函数:

-- main.lua
local calc = require("calc")

print(calc.add(10, 2))   -- 12

没有涉及花哨的语法,也没有引入新的专用关键字 - 这种简化的模块系统可以在任何复杂的Lua项目中使用!


结论

在这个故事中,我们通过开发实用的Lua代码示例探索了Lua脚本语言的简单性。Lua是一种适合初学者的语言,具有最小的语法、少量的数据类型、仅一个内置数据结构和一个简单的标准库。它还是一种功能齐全的语言,支持非抢占式多线程,并提供了一个精简但功能齐全的标准库。Lua社区开发了一个JIT编译器、一个托管数千个开源模块的包管理器,以及各种C库的绑定,因此使用Lua构建生产软件系统是完全可能的。

然而,Lua是一种被低估的语言,只有游戏开发人员知道。然而,作为一种最小化的、动态类型的脚本语言,它有潜力与Python和Ruby竞争。任何人都可以在几分钟内学会Lua,因为它是有史以来最简单、功能最齐全的编程语言!

免责声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。

相关文章

探索多种软件架构模式及其实用应用
2024年11月22日19:06
本文深入探讨了多种软件架构模式,包括有界上下文、边车模式、发布-订阅模式、应用网关、微服务、命令职责分离(CQRS)等,介绍了它们的优点、使用场景以及具体应用实例。文章强调根据具体项目需求和团队能力选择最合适的架构,以构建高效和可维护的解决方案,同时展示了各架构模式间的综合应用,提供了丰富的案例和技术细节。
15个高级Python快捷键助您更快编程
2024年11月21日07:02
本文分享了 15 个高级的 Python 编程快捷键,包括上下文管理器、行内字典合并、函数参数解包、链式比较、dataclasses、海象运算符、反转列表、备忘录缓存、splitlines、enumerate、字典推导、zip 用于并行迭代、itertools.chain 扁平化列表、functools.partial 部分函数和 os.path 文件路径管理等,帮助开发者提高编程效率和代码简洁性。
揭示网页开发的 11 个迷思:停止相信这些误区
2024年11月19日22:05
网页开发充满误解,这篇博文针对11个常见迷思进行揭秘。包括网站开发后不需更新、需掌握所有技术、AI会取代开发者等。强调持续学习、专业化、用户体验的重要性,澄清误区如多任务处理的必要性和最新技术的必需性。文章提醒开发者注重实用而非追求完美代码,以务实态度面对开发工作。
你知道 CSS 的四种 Focus 样式吗?
2024年11月18日21:41
本文介绍了四种 CSS focus 样式::focus、:focus-visible、:focus-within 以及自定义的 :focus-visible-within,帮助提升网站用户体验。:focus 样式应用于被选中元素;:focus-visible 仅在键盘导航时显示;:focus-within 用于父元素;自定义 :focus-visible-within 结合两者效果。合理运用这些样式能使网站更方便键盘用户导航。
利用 Python 实现自动化图像裁剪:简单高效的工作流程
2024年11月11日20:49
使用 Python 和 OpenCV 自动裁剪图像,轻松实现 16:9 的完美构图。这个指南介绍了如何通过代码进行灰度化、模糊处理和边缘检测,最终识别出最重要的部分进行裁剪。特别适合需要批量处理图像的情况,节省大量时间。
每位资深前端开发人员都应了解的 TypeScript 高级概念
2024年11月11日02:07
资深前端开发者应了解 TypeScript 的高级概念,如联合类型、交叉类型、类型保护、条件类型、映射类型、模板字面量类型和递归类型。这些特性可提升代码的可维护性和可扩展性,确保在开发复杂应用时实现更高的类型安全性和效率。