LUA语法细节

1. 使用 nil 作比较时应该加上双引号:

> type(X)
nil
> type(X)==nil
false
> type(X)=="nil"
true

2. Lua 把 false 和 nil 看作是"假",其他的都为"真":

if false or nil then
    print("至少有一个是 true")
else
    print("false 和 nil 都为 false")
end

if 0 then
    print("数字 0 是 true")
else
    print("数字 0 为 false")
end

3. Lua 默认只有一种 number 类型 -- float(双精度)类型(可编译为lua精简模式,将编译为32位整型和单精度类型),最大可表示2^53的整型值,即lua5.2的最大可表示整型值。而Lua5.3开始提供integer类型:64位整型。

math.type(3)     -->integer,最大值2^63-1,约等于10^19
math.type(3.0)     -->float

随机数:

math.random()    -->[0,1)

math.random(n)    -->[1,n]

math.random(l,u)    -->[l,u]

math.randomseed(os.time())  -->使用当前系统时间作为随机数发生器的初始化种子
math.maxinteger+1 == math.mininteger -->true

 math.tointeger()会在输入参数无法转化为整型时返回nil:

function cond2int (x)         -->若无法转换为整型,就保持不变
    return math.tointeger(x) or x
end

 

4. 字符串由一对双引号或单引号来表示,也可以用 2 个方括号 "[[]]" 来表示"一块"字符串(常用于表示html带格式代码块)。字符串连接使用的是 ..

  使用 # 来计算字符串的长度,放在字符串前面,如下实例:

> len = "www.w3cschool.cn"
> print(#len)
16
> print(#"www.w3cschool.cn")
16

5. Lua 中的表(table)其实是一个键值对数组(Map),数组的key可以是数字或者是字符串。初始索引默认以 1 开始,不是0。

a3 = {}
for i = 1, 10 do
    a3[i] = i
end
a3["key"] = "val"
print(a3["key"])
print(a3["none"])

 6. 对 table 的索引使用方括号 []。当索引为字符串类型时,可以用 . 操作。 

> site = {}
> site["key"] = "www.w3cschool.cn"
> print(site["key"])
www.w3cschool.cn
> print(site.key)
www.w3cschool.cn

7. 可以对多个变量同时赋值,变量之间、值之间以“,”分开,相当于分别赋值的多条赋值语句;右边的值会依次赋给左边的变量。

a, b = 10, 2*x       <-->       a=10; b=2*x

遇到赋值语句Lua会先计算右边所有的值然后再执行赋值操作,所以我们可以这样进行交换变量的值:

x, y = y, x                     -- swap 'x' for 'y'
a[i], a[j] = a[j], a[i]         -- swap 'a[i]' for 'a[j]'

右边值少则赋nil值给左边多出的变量,值多会被舍弃。注意:如果要对多个变量赋值必须依次对每个变量赋值。

a, b, c = 0, 1
print(a,b,c)             --> 0   1   nil
 
a, b = a+1, b+1, b+2     -- value of b+2 is ignored
print(a,b)               --> 1   2

  a, b, c = 0
  print(a,b,c) --> 0 nil nil

a, b, c = 0, 0, 0
print(a,b,c)             --> 0   0   0

8. Lua函数可以返回多个结果值:

> s, e = string.find("www.w3cschool.cn", "w3cschool") 
> print(s, e)
5    13

Lua函数中,在return后列出要返回的值得列表即可返回多值:

function maximum (a)
    local mi = 1             -- 最大值索引
    local m = a[mi]          -- 最大值
    for i,val in ipairs(a) do
       if val > m then
           mi = i
           m = val
       end
    end
    return m, mi
end

print(maximum({8,10,23,12,5}))

在函数参数列表中使用三点(...) 表示函数有可变的参数,用#arg表示获取其长度:(#运算符:一元运算符,返回字符串或表的长度。)

function average(...)
   result = 0
   local arg={...}
   for i,v in ipairs(arg) do
      result = result + v
   end
   print("总共传入 " .. #arg .. " 个数")
   return result/#arg
end

print("平均值为",average(10,5,3,4,5,6))

-- 总共传入 6 个数
-- 平均值为 5.5

9. 运算符优先级,以下从高到低:

^
not    - (unary)
*      /
+      -
..
<      >      <=     >=     ~=     ==
and
or

除了^和..外所有的二元运算符都是左连接的。

10. Lua 提供的字符串的操作API:upper/lower/find/reverse/format/char/byte/len/rep/sub/gsub

string.sub(s, i [, j]) 用于截取字符串:

-- 字符串
local sourcestr = "prefix--runoobgoogletaobao--suffix"
local second_sub = string.sub(sourcestr, 1, 8)    --"prefix--"
-- 截取最后10个
local third_sub = string.sub(sourcestr, -10)    --"ao--suffix"
-- 索引越界,输出原始字符串
local fourth_sub = string.sub(sourcestr, -100)    --"prefix--runoobgoogletaobao--suffix"

s = "Deadline is 30/05/1999, firm"
date = "%d%d/%d%d/%d%d%d%d"
print(string.sub(s, string.find(s, date)))    --> 30/05/1999

string.gsub(mainString,findString,replaceString,num)

在字符串mainString中,找到findString后replaceString,替换num次。

string.gmatch(str, pattern)
返回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。

> for word in string.gmatch("Hello Lua user", "%a+") do print(word) end
Hello
Lua
user

string.match(str, pattern, init)
string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。
在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。

> = string.match("I have 2 questions for you.", "%d+ %a+")
2 questions

> = string.format("%d, %q", string.match("I have 2 questions for you.", "(%d+) (%a+)"))
2, "questions"

 

字符串格式化

Lua 提供了 string.format() 函数来生成具有特定格式的字符串, 函数的第一个参数是格式 , 之后是对应格式中每个代号的各种数据。

由于格式字符串的存在, 使得产生的长字符串可读性大大提高了。这个函数的格式很像 C 语言中的 printf()。

以下实例演示了如何对字符串进行格式化操作:

格式字符串可能包含以下的转义码:

  • %c - 接受一个数字, 并将其转化为ASCII码表中对应的字符
  • %d, %i - 接受一个数字并将其转化为有符号的整数格式
  • %o - 接受一个数字并将其转化为八进制数格式
  • %u - 接受一个数字并将其转化为无符号整数格式
  • %x - 接受一个数字并将其转化为十六进制数格式, 使用小写字母
  • %X - 接受一个数字并将其转化为十六进制数格式, 使用大写字母
  • %e - 接受一个数字并将其转化为科学记数法格式, 使用小写字母e
  • %E - 接受一个数字并将其转化为科学记数法格式, 使用大写字母E
  • %f - 接受一个数字并将其转化为浮点数格式
  • %g(%G) - 接受一个数字并将其转化为%e(%E, 对应%G)及%f中较短的一种格式
  • %q - 接受一个字符串并将其转化为可安全被Lua编译器读入的格式
  • %s - 接受一个字符串并按照给定的参数格式化该字符串

为进一步细化格式, 可以在%号后添加参数. 参数将以如下的顺序读入:

  • (1) 符号: 一个+号表示其后的数字转义符将让正数显示正号. 默认情况下只有负数显示符号.
  • (2) 占位符: 一个0, 在后面指定了字串宽度时占位用. 不填时的默认占位符是空格.
  • (3) 对齐标识: 在指定了字串宽度时, 默认为右对齐, 增加-号可以改为左对齐.
  • (4) 宽度数值
  • (5) 小数位数/字串裁切: 在宽度数值后增加的小数部分n, 若后接f(浮点数转义符, 如%6.3f)则设定该浮点数的小数只保留n位, 若后接s(字符串转义符, 如%5.3s)则设定该字符串只显示前n位.

 11. 向数组中添加元素:

-- 创建一个数组
local myArray = {10, 20, 30, 40, 50}

-- 添加新元素到数组末尾
myArray[#myArray + 1] = 60

-- 删除第三个元素
table.remove(myArray, 3)

多维数组

-- 初始化数组
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

12. 常用Table 操作API:concat /insert/remove/sort

fruits = {"banana","orange","apple","grapes"}
print("排序前")
for k,v in ipairs(fruits) do
        print(k,v)
end

table.sort(fruits)
print("排序后")
for k,v in ipairs(fruits) do
        print(k,v)
end

结果:

排序前
1    banana
2    orange
3    apple
4    grapes
排序后
1    apple
2    banana
3    grapes
4    orange

13. LUA模块:

require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。

当然,如果没有 LUA_PATH 这个环境变量,也可以自定义设置,在当前用户根目录下打开 .profile 文件(没有则创建,打开 .bashrc 文件也可以),例如把 "~/lua/" 路径加入 LUA_PATH 环境变量里:

C包

与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。

Lua在一个叫loadlib的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。所以典型的调用的例子如下:

local path = "/usr/local/lua/lib/libluasocket.so"
local f = loadlib(path, "luaopen_socket")
local path = "/usr/local/lua/lib/libluasocket.so"
-- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下
local f = assert(loadlib(path, "luaopen_socket"))
f()  -- 真正打开库

 14. LUA元表 metatable

__index元方法:在访问某键但不存在时,Lua会寻找该table的metatable中的__index 键。如果__index包含一个表格,Lua会在表格中查找相应的键。

__newindex 元方法(__index则用来对表访问 ,__newindex用来对表更新)。给表的一个缺少的索引赋值,解释器就会查找__newindex 元方法:如果存在则调用这个函数替代赋值操作(即不赋值)。

__call 元方法在 Lua 调用一个值时调用。以下实例演示了计算表中元素的和:

__tostring 元方法用于修改表的输出行为。以下实例我们自定义了表的输出内容:

 15. 协程:yield与resume: 【详见https://www.runoob.com/lua/lua-coroutine.html】

coroutine.creat方法只要建立了一个协程 ,那么这个协程的状态默认就是suspend。使用resume方法启动后,会变成running状态;遇到yield时将状态设为suspend;如果遇到return,那么将协程的状态改为dead。

coroutine.resume方法需要特别注意的一点是,这个方法只要调用就会返回一个boolean值。

coroutine.resume方法如果调用成功,那么返回true;如果有yield方法,同时返回yield括号里的参数;如果没有yield,那么继续运行直到协程结束;直到遇到return,将协程的状态改为dead,并同时返回return的值。

coroutine.resume方法如果调用失败(调用状态为dead的协程会导致失败),那么返回false,并且带上一句"cannot resume dead coroutine"

function yieldReturn(arg) return arg end

co_yieldtest = coroutine.create(
    function()
        print("启动协程状态"..coroutine.status(co_yieldtest))
        print("--")
        coroutine.yield()
        coroutine.yield(1)
        coroutine.yield(print("第3次调用"))
        coroutine.yield(yieldReturn("第4次调用"))
        return 2
    end
)

    print("启动前协程状态"..coroutine.status(co_yieldtest))
    print("--")

for i = 1,6 do
    print(""..i.."次调用协程:", coroutine.resume(co_yieldtest))
    print("当前协程状态"..coroutine.status(co_yieldtest))
    print("--")
end

输出:

启动前协程状态suspended
--
启动协程状态running
--
第1次调用协程:    true
当前协程状态suspended
--
第2次调用协程:    true    1
当前协程状态suspended
--
第3次调用
第3次调用协程:    true
当前协程状态suspended
--
第4次调用协程:    true    第4次调用
当前协程状态suspended
--
第5次调用协程:    true    2
当前协程状态dead
--
第6次调用协程:    false    cannot resume dead coroutine
当前协程状态dead
--

再看一例:

print();
cor = coroutine.create(function(a)
    print("参数 a值为:", a);
    local b, c = coroutine.yield(a + 1); --这里表示挂起协程,并且将a+1的值进行返回,并且指定下一次唤醒需要 b,c 两个参数。
    print("参数 b,c值分别为:", b, c);
    return b * c; --协程结束,并且返回 b*c 的值。
end);

print("第一次调用:", coroutine.resume(cor, 1));
print("第二次调用:", coroutine.resume(cor, 2, 2));
print("第三次调用:", coroutine.resume(cor));

输出:

参数 a值为:    1
第一次调用:    true    2
参数 b,c值分别为:    2    2
第二次调用:    true    4
第三次调用:    false    cannot resume dead coroutine

 16. LUA继承:

classA={}        --self
function classA:getob(name)
    print(self)
    ob = {name = name}            -- 这里的属性值要赋给新建表ob而不是老表self;若像有的程序错误赋给self的话,下面打印(c1/c2.name)将输出一样的值,即最后赋给它的值。
    setmetatable(ob, {__index=self})    --元表设置可以让子类能引用到父类的方法
    return ob
end

function classA:getself()
    return self
end

c1=classA:getob("A")
c2=classA:getob("B")
print(string.rep("*",30))
print(c1:getself())
print(c1.name)              --上面构造函数里不能将构造参数赋给self,不然值会都一样
print(c2:getself())
print(c2.name)              --上面构造函数里不能将构造参数赋给self,不然值会都一样
print(string.rep("*",30))

----------------------继承------------------------

classB=classA:getob()    ----非常重要,用于获取继承的self
print(string.rep("-",30))
function classB:getob(name,address)
    ob=classA:getob(name)
    setmetatable(ob,self)
    self.__index=self
    ob.address=address
    return ob
end

c3=classB:getob("gray.yang","shenzhen")
print(c3:getself())

输出结果:

table: 00000000001b9930                    --指向最初的老表
table: 00000000001b9930                    --指向最初的老表
******************************
table: 00000000001b9d70
A
table: 00000000001b9970
B
******************************
table: 00000000001b9930                    --指向最初的老表
------------------------------
table: 00000000001b9930                    --只想最初的老表
table: 00000000006910c0

 

posted @ 2025-11-30 17:56  云山漫卷  阅读(9)  评论(0)    收藏  举报