Lua 基础语法学习和使用

本文介绍Lua语言的基础知识,包括变量、数据类型、流程控制等内容,并详细解释了Lua中的迭代器、循环函数、table模块等特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >




了解 Lua

  • 脚本语言,嵌入程序中,为应用程序提供灵活的扩展和定制功能
  • 轻量级:使用C语言编写,编译后只有一百余K,可以很方便嵌入别的程序中
  • 可扩展:lua 可以调用 C/C++ 提供的一些功能
  • 支持面向过程编程 和 函数式编程
  • 自动内存管理
  • 动态语言类型。
    • 变量不要类型定义,只需要为变量赋值。



变量

  • 变量:全局变量、局部变量、表中的域
    • 变量的默认值:nil
  • 局部变量:只有用 local 显式声明的为局部变量。作用域从声明位置开始直到所在的语句块结束
  • 全局变量:不需要声明,赋值后即创建了。
    • 访问没有初始化的全局变量不会报错,得到的是nil
    • 想删除一个全局变量,将其赋值为nil即可

  • 尽量使用局部变量:
    • 避免命名冲突
    • 访问局部变量的速度比全局变量更快



数据类型

Lua 中有 8 个基本类型:

  • nil
    • 表示一个无效值(在条件表达式中等价于false)
  • boolean
  • number
    • 默认 double 双精度
  • string
  • function
    • 由 C 或 Lua 编写的函数
  • userdata
    • 表示任意存储在变量中的 C 数据结构
    • userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型。
      可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。
  • thread
    • 执行的独立路线,用于执行协同程序
  • table
    • 通过构造表达式来创建,比如{}来创建一个空表



print 函数

print("hello world")
print(type("hello world"))      -- type :输出变量的类型
print(#"# can get len")         -- #:获取字符串的长度,13


-- nil 作比较时应该加上双引号 ""
print(type(x) == nil)           -- false
print(type(x) == "nil")         -- true


-- false 和 nil ------> false
print(type(true))               -- boolean
print(type(false))              -- boolean
print(type(nil))                -- nil


-- 在对数字字符串进行运算操作时,会尝试将这个数字字符串转成一个数字
print("2" + 6)              -- 8



多变量赋值

Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋值给左边的变量
赋值语句会先计算右边所有的值后 在 进行赋值操作,因此可以交换变量的值

x = 1
y = 2
x , y = y , x 
print(x , y)        -- 2  1

当变量个数 > 值的个数------按变量个数补足nil

x , y = 2
print(x , y)        -- 2  nil(注意这里 y 的值变成 nil 了)



数组

-- 初始化数组
array = {}
for i = 1 , 3 do
    array[i] = {}
    for j = 1 , 3 do
        array[i][j] = i * j
    end
end
-- 访问数组
for i = 1 , 3 do
    for j = 1 , 3 do
        print(array[i][j])
    end
end



流程控制

local f = 12
if f < 0 then
    print("f < 0")
elseif f < 4 then
    print("0 <= f < 4")
else
    print("f >= 4")
end



迭代器

迭代器是一种对象,能够用来遍历标准模板库容器中的部分或者全部元素,每个迭代器对象代表容器中的确定的地址
迭代器是一种支持指针类型的结构,可以遍历集合的每一个元素,



无状态迭代器

无状态迭代器不保留任何状态的迭代器,在循环中可以利用无状态迭代器避免创建闭包花费额外的代价。
每一次迭代,迭代函数都是用两个变量(状态常量 和 控制变量)的值作为参数被调用,一个无状态的迭代器只利用这两个值可以获取下一个元素。
无状态迭代器的典型的例子就是 ipairs。

-- 这里要注意两个参数的顺序 状态常量   控制变量
function test(iterMaxCount , currNum)
    if currNum <= iterMaxCount then
        currNum = currNum + 1
        return currNum , currNum * currNum
    end
end
-- 迭代函数test  状态常量  控制变量
for i , n in test, 3, 1 do
    print(i , n)
end
--[[
输出:
2       4
3       9
4       16
]]


泛型 for
1: 在自己内部保存迭代函数,实际上它保存三个值:迭代函数状态常量控制变量
2: 使用默认的迭代函数:ipairs

-- 第一次迭代调用iter(a , 0),返回 1 , a[1]
-- 第二次迭代调用iter(a , 1),返回 2 , a[2]
-- ......直到得到一个nil元素
function iter( a , i)
    i = i + 1                -- 下标
    local value = a[i]       -- 读取数组 a 中对应下标是 i 的值
    if value then
        return i , value 
    end
end
function ipairs(a)
    return iter , a , 0     -- 迭代函数iter  状态常量a  控制变量初始值0
end




多状态迭代器

很多情况下,迭代器需要保存多个状态信息,而不是简单的状态常量 和 控制变量,
最简单的方法是使用闭包,或者将所有的状态信息封装到 table 内,将table作为迭代器的状态常量,因为所有的信息都存储到table内,因此迭代函数通常不需要第二个参数

array = {"hello" , "you"}
function elemIter(arr)
    local index = 0
    local len = #arr

    -- 闭包函数
    return function()
        index = index + 1
        if index <= len then
            return arr[index]
        end
    end
end
for element in elemIter(array) do
    print(element)
end
--[[
输出:
hello
you
]]



循环

-- while 循环
a = 10
while(a < 12) do
    print("a 的值为:", a)
    a = a + 1
end


-- for循环的三个表达式会在循环开始前,一次性求值,以后不在求值
for i = 11 , a , 2 do
    print("i 的值为:", i)
end


-- 泛型 for 循环。通过一个迭代器函数来遍历所有值
taba = {"one" , "two" , "three"}
for i , v in ipairs(taba) do
    print( i , v)
end
--[[
输出:
    1       one
    2       two
    3       three
]]


-- repeat.....until
b = 10
repeat
    print("b 的值为:" , b)
    b = b + 1
until( b > 13)
--[[
输出:
    b 的值为:      10
    b 的值为:      11
    b 的值为:      12
    b 的值为:      13
]]


-- break:用于退出当前循环或语句。如果使用循环嵌套,break 语句将停止 最内层 循环的执行

 



函数

myprint = function(param)
    print("这是自定义打印函数: " , param)
end
myprint(10)                 -- 这是自定义打印函数: 10
function add(num1  , num2 , functionparam)
    result = num1 + num2
    functionparam(result)
end
add(2 ,5 , myprint)         -- 这是自定义打印函数: 7



可变参数

-- 可变参数,使用 ... 表示
-- 可以通过select("#" , ...) 来获取可变参数的数量
function add(...)
    local s = 0
    for i , v in ipairs{...} do
        s = s + v
    end
    print("可变参数的参数个数为" , select("#" , ...))
    return s
end
print("总和为:" , add(3, 4, 5, 6, 7))


-- 可变参数可以赋值给一个变量 arg ,可以通过 #arg 获取可变参数的数量
function average(...)
    local s = 0
    local arg = {...}

    for i , v in ipairs(arg) do
        s = s + v
    end
    print("可变参数的参数个数为" , #arg)
    return s/#arg
end
print("平均值为:" , average(3, 4, 5, 6, 7))



固定参数 + 可变参数

-- 固定参数 + 可变参数
function fwrite(fmt , ...)
    return io.write(string.format(fmt , ...))
end
fwrite("hello you\n")           -- hello you
fwrite("%d%d\n" , 1 , 2)        -- 12


-- 使用 select 来访问变长参数,select("#" , ...) 或者 select(n , ...)
function foo(...)
    for i = 1 , select("#" , ...) do
        local arg = select(i , ...)   -- 读取参数
        print("arg: " , arg)
    end
end
foo(1 ,2 ,3 ,4 )
--[[
输出:
    arg:    1
    arg:    2
    arg:    3
    arg:    4
]]



匿名函数

function testFun( tab , fun)
    for k , v in pairs(tab) do
        print(fun(k , v))
    end
end
-- 调用testFun,第二个参数使用匿名函数的形式
tab={ key1 = "val1" , key2 = "val2"};
testFun(tab , 
             function(key , val)
                return key.."="..val
             end
        );



table

table 使用关联型数组,可以使用任意类型的值来作为数组的索引,但这个值不能是 nil
table 是不固定大小的,可以根据自己的需要进行扩容


构造器是创建和初始化表 的表达式,最简单的构造函数是 {} ,用来创建一个空表

-- 初始化表
mytable = {}

mytable[1] = "lua"

--移除引用,后续lua垃圾回收会释放对应内存
mytable = nil


lua 表中默认索引从 1 开始

tabl = { key1 = "val1" , key2 = "val2" , "val3"}
for k ,v in pairs(tabl) do
    print(k .. "-" .. v .. " , " .. type(k) .. "-" .. type(v))
end
tabl.key1 = nil
--[[
输出:
    1-val3
    key1-val1
    key2-val2
]]--


获取表的长度
可以发现使用 # 或者 table.getn 获取表的长度的时候,都会在索引中断的地方停止计数,而导致无法正确获取table的长度

table1 = {[1] = 2 , [2] = 6 ,[3] = 34 , [26] = 5}
print("使用 # 获取table1 的长度:" , #table1)							-- 3
print("使用 table.getn 获取table1 的长度:" , table.getn(table1))  	-- 3

function getLen(t)
	local len = 0
	for k , v in pairs(t) do
		len = len + 1
	end

	return len
end
print("使用 自定义函数 获取table1 的长度:" , getLen(table1))   --4 



表之间的赋值

mytable = {}
mytable[1]="Lua"
mytable["haha"]="hello"

altertable = mytable

-- 两个table都指向同一块内存,修改会同步修改
altertable[1]="Lua new"
print(mytable[1] , altertable[1])		-- Lua new Lua new

-- 一个table赋值为nil后不影响另一个table所指向的内容
altertable = nil
print(mytable[1])						-- Lua new


table 操作

concat

mytable = {"banana" , "orange" , "apple"}
print("连接后的字符串" ,table.concat(mytable))
print(", 连接后的字符串" ,table.concat(mytable , ","))
print(", 连接部分数据后的字符串" ,table.concat(mytable , "," , 2 , 3))

--[[
输出:
连接后的字符串  bananaorangeapple
, 连接后的字符串        banana,orange,apple
, 连接部分数据后的字符串        orange,apple
]]

sort

print("排序前:")
for k , v in pairs(mytable) do
	print(k , v)
end

table.sort(mytable)

print("排序后:")
for k , v in pairs(mytable) do
	print(k , v)
end

--[[
输出:
1       banana
2       orange
3       apple
排序后:
1       apple
2       banana
3       orange
]]



模块

模块类似一个封装库,把公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
模块是由变量、函数等组成的 table,创建一个模块就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。

比如我们现在定义一个模块叫做module,书写在module.lua文件中。在usemodule.lua 中调用模块module的变量和函数:
module.lua

-- 定义一个名字为 model 的模块
module = {}

module.constant = "这是一个常量"

-- 定义一个公有函数
function module.func1()
	io.write("这是一个公有函数\n")
end

-- 定义一个私有函数
-- local修饰的函数。即不能从外部访问模块里的这个私有函数,必须通过模块里的 公有函数来调用,如func3
local function func2()
	print("这是一个私有函数\n")
end

-- 定义一个共有函数,调用私有函数
function module.func3()
	func2()
end

-- 返回封装好的 table
return module

usemodule.lua

-- 可以通过require来加载模块
require("module")
print(module.constant)
module.func3()


-- 也可以给加载的模块定义一个别名
local m = require("module")
print(m.constant)
m.func3()



动态链接库

Lua 在一个叫 loadlib 函数中提供了所有的动态链接的功能。这个函数有两个参数:库的绝对路径初始化函数

loadlib 函数
加载指定的库并连接到 Lua,但是并不打开库(也就是没有调用初始化函数),但是返回初始化函数作为 Lua 的一个函数,这样就能直接在 Lua 中调用他。

local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")  -- 并不打开库,而是返回一个初始化函数,这个函数可以在 lua 中调用
f()  -- 真正打开库



元表

在 Lua 中对 table 可以通过访问对应的 key 来得到 value 值,但是却无法对两个 table 进行操作。
Lua 提供了元表( Metatable),允许改变 table 的行为,每个行为关联了对应的元方法。




协同程序(coroutine)

拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。
线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值