Lua入门学习

Lua基本数据类型

数据类型 描述
nil 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean 包含两个值:false和true。
number 表示双精度类型的实浮点数
string 字符串由一对双引号或单引号来表示
function 由 C 或 Lua 编写的函数
userdata 表示任意存储在变量中的C数据结构
thread 表示执行的独立线路,用于执行协同程序
table Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。
print(type("Hello world"))      -- string
print(type(10.4*3))             -- number
print(type(print))              -- function
print(type(type))               -- function
print(type(true))               -- boolean
print(type(nil))                -- nil
print(type(type(X)))            -- string

Lua赋值

local a,b,c = 0, "abc", true
print(a,b,c)    --输出: 0 abc true   结论1
A,B,C = 1, "ABC", false
print(A,B,C)    --输出: 1 ABC false  结论1

local x,y = 0     --0 nil     结论2
X,Y = 0           --0 nil     结论2

local m,n = 0,1,2
M, N = 0,1,2
print(m,n)      --输出:0 1  结论3
print(M,N)      --输出:0 1  结论3

local m, n = 1, 2
print(m, n)       --1 2 结论4
m, n = n, m
print(m, n)       --2 1 结论4
  1. 局部变量和全局变量都可以多变量同时赋值

  2. 变量的个数多于值的个数时,按照变量个数补nil

  3. 变量的个数少于值的个数时,会忽略多于的值

  4. 执行赋值计算时,会优先进行右边的数值,再进行赋值操作,可进行快速交换两个变量的值

特殊全局变量_G

_G是Lua内置的全局变量,其类型是一个表table,用于保存所有其他的全局变量。类似于JS中的window对象。

Lua运算符

算术运算符

操作符 描述 实例
+ 加法 A + B 输出结果 30
- 减法 A - B 输出结果 -10
* 乘法 A * B 输出结果 200
/ 除法 B / A 输出结果 2
% 取余 B % A 输出结果 0
^ 乘幂 A^2 输出结果 100
- 负号 -A 输出结果 -10

关系运算符

操作符 描述 实例
== 等于,检测两个值是否相等,相等返回 true,否则返回 false (A == B) 为 false。
~= 不等于,检测两个值是否相等,不相等返回 true,否则返回 false (A ~= B) 为 true。
> 大于,如果左边的值大于右边的值,返回 true,否则返回 false (A > B) 为 false。
< 小于,如果左边的值大于右边的值,返回 false,否则返回 true (A < B) 为 true。
>= 大于等于,如果左边的值大于等于右边的值,返回 true,否则返回 false (A >= B) 返回 false。
<= 小于等于, 如果左边的值小于等于右边的值,返回 true,否则返回 false (A <= B) 返回 true。

逻辑运算符

操作符 描述 实例
and 逻辑与操作符。 若 A 为 false,则返回 A,否则返回 B。 (A and B) 为 false。
or 逻辑或操作符。 若 A 为 true,则返回 A,否则返回 B。 (A or B) 为 true。
not 逻辑非操作符。与逻辑运算结果相反,如果条件为 true,逻辑非为 false。 not(A and B) 为 true。

其他运算符

操作符 描述 实例
.. 连接两个字符串 a..b ,其中 a 为 "Hello " , b 为 "World", 输出结果为 "Hello World"。
# 一元运算符,返回字符串或表的长度。 #"Hello" 返回 5

运算符优先级

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

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

Lua分支

--[[if]]
if 布尔表达式 then
    --true时触发
end

--[[if...else]]
if 布尔表达式 then
    --true时触发
else
    --false时触发
end

--[[if..elseif...else]]
if 布尔表达式 then
    --true时触发
elseif
    --true时触发
else
    --false时触发
end  

PS:lua的条件判断和JS不同的是,当条件判断为0时,例if a then此时a=0,当前的判断为true类型

Lua中没有switch,也没有三名运算符

Lua循环

local a,b=0,0
--while循环
while a<10 do
    a=1+a
    print("a="..a)
end
--repeat...until
repeat
    b=b+2
    print("b="..b)
until b>10
--for
for i=0,3,1 do
    print("这是第"..(i+1).."次for循环")
end

Lua函数

function foo(a) print("这是foo函数,参数为" .. a) end
foo(123)

function foo1(a)
    a = a + 14
    return (a)
end
print(foo1(16))

function foo2()
    local a, b, c = 1, "b", false
    return a, b, c
end
a, b, c, d = foo2()
print(a, b, c, d)

PS:多返回值时,接收函数返回值的语句类似多变量赋值。如果变量多于函数返回值的数目,则用nil补全;如果变量少于函数返回值的数目,则忽略剩余返回值。

Lua表

数组

array = {"lua", "Tuo"}
for i = 0, 2 do print(array[i]) end--nil lua Tuo

local arr1 = {1, 2, 3, nil, 5}
for k, v in ipairs(arr1) do print(k, v) end--1 1 2 2 3 3
for i, v in pairs(arr1) do print(i, v) end--1 1 2 2 3 3 5 5
            
local arr = {{1, 2}, {2, 3}}
for i = 1, 2 do 
    for j = 1, 2 do 
        print(arr[i][j]) 
    end 
end
  • lua数组索引值从1开始
  • ipairs遍历时只会遍历到nil之前的元素,而使用pairs遍历就会跳过nil元素,继续遍历后面的元素

元组

 arr={1,"lua",nil,{1,2}}
 for k,v in pairs(arr) do print(k,v) end

元组和数组的差别就是,元组表内的元素的类型不尽相同。

元组声明和赋值的方法,以及遍历的方法和数组一样,这里不再赘述

字典

local obj = {x = 1, y = 2, z = 3}
for k, v in pairs(obj) do print(k, v) end --x 1 z 3 y 2
for k, v in ipairs(obj) do print(k, v) end --

PS:pairs遍历的顺序没法确定,即不是赋值的顺序,也不是键的自然排序顺序。iparis无法遍历到字符串索引。

local obj2 = {}
obj2.x = 1
obj2["y"] = 2
obj2.z = 3

可以通过[].来访问和赋值字典属性

元表

__index是元表上的一个属性,这个属性可以指向一个表,也可以指向一个函数。它的作用是,当通过键来访问table的时候,如果在table上找不到键对应的值,就会查找table的元表上的__index指向的表上是否存在键对应的值,如果__index指向一个函数,这是调用这个函数。

local obj={}
local myMetatable = {name="metatable"}
setmetatable(obj,myMetatable)
print(obj.name)--nil,obj上没有name对应的值
myMetatable.__index =myMetatable;
print(obj.name)--metatable,obj元素的__index指向了myMetatable,而myMetatable上name有对应的值

PS:这里myMetatable.__index可以指向任意对象,并非一定要指向自身。但是如果指向obj,则会形成死循环报错。

local obj = {}
local myMetatable = {name = "metatable"}
setmetatable(obj, myMetatable)
print(obj.name)--nil
myMetatable.__index = function(obj, key)
    print(obj, key)--obj内存地址 键名
    if key == "name" then return "__index is a function" end
end
print(obj)--内存地址
print(obj.name)-- __index is a function
print(rawget(obj, "name")) --nil

通过rawget方法去访问目标表上的属性时,就会忽略目标表的元表的__index属性的,只从目标表上查找属性。

__newindex是元表上的一个属性,指向一个表。它的作用是,当通过键值向table添加一个属性时,属性实际会添加到__newindex指向的这个表上。

local obj = {}
local myMetatable = {}
setmetatable(obj, myMetatable)
obj.name = "add name"
print(obj.name)--add name
myMetatable.__newindex = myMetatable
obj.id = "add id"
print(obj.id)--nil
print(myMetatable.id)--add id

上例中,myMetatableobj的元表。

__newindex没有指向时,向obj添加name属性时,就直接是添加到了obj上,因此obj.name是 “add name”;

__newindex指向myMetatable这个表时,向obj添加id属性时,就是添加到了myMetatable上,因此obj.id是 nil,而myMetatable.id 是 “add id”。

local obj = {}
local myMetatable = {}
setmetatable(obj, myMetatable)
myMetatable.__newindex = function(obj, key, value) print(obj, key, value) end
obj.name = "a"--table: 0000000002572810 name    a
obj.name = "b"--table: 0000000002572810 name    b
obj.name = "c"--table: 0000000002572810 name    c

每次给name属性赋值,就会调用一次元表的__newindex方法。这是由于每次给obj增加属性,属性实际并没有添加到obj

如果给__newindex方法里添加一行rawset方法,那么就只会打印第一行了,即__newindex方法只调用一次。

myMetatable.__newindex = function(obj, key, value)
    rawset(obj, key, value)
    print(obj, key, value)
end

rawset方法的作用就是,忽略目标表的元表上的__newindex属性,直接给目标表添加属性和值。

rawset对应的还有一个rawget,他的左右就是忽略目标表的元表上的__index属性,直接从目标表上查找属性并返回

__tostring指向一个函数,返回一个字符串。用于处理表如何转换为字符串

local obj = {}
local myMetatable = { }
print(obj)   --输出结果:obj的内存地址
setmetatable(obj, myMetatable)
myMetatable.__tostring = function(obj)
  return "print obj"
end
print(obj)   --输出结果:print obj

__call指向一个函数,当将table像函数一样调用时,就会调用此方法

local obj = {}
local myMetatable = { }
print(obj)      --输出结果: obj的内存地址
setmetatable(obj, myMetatable)
myMetatable.__call = function(obj, args1, args2)
    print("__call invoked, args: ",obj, args1, args2)
end
obj()--__call invoked,args: table: 00000000025E58F0 nil nil
obj(1) --__call invoked,args: table: 00000000025E58F0 1 nil
obj("ab", true)--__call invoked,args: table: 00000000025E58F0 ab    true

上例中,把obj当成函数一样调用,并且可以传入任意的参数。__call函数的第一个参数总是obj自身,后续的参数则是调用时传入的。

__call也可以指向带返回值的函数

元表自定义运算符

模式 描述
__add 对应的运算符 '+'.
__sub 对应的运算符 '-'.
__mul 对应的运算符 '*'.
__div 对应的运算符 '/'.
__mod 对应的运算符 '%'.
__unm 对应的运算符 '-'.
__concat 对应的运算符 '..'.
__eq 对应的运算符 '=='.
__lt 对应的运算符 '<'.
__le 对应的运算符 '<='.
local obj = {}
local myMetatable = { }
setmetatable(obj, myMetatable)

myMetatable.__mul = function(left,right)
    return 100
end

myMetatable.__unm = function(left,right)
    return -0.5
end

myMetatable.__concat = function(left,right)
    return "-left.right-"
end

myMetatable.__eq = function(left,right)
    return true
end

myMetatable.__lt = function(left,right)
    return true
end

myMetatable.__le = function(left,right)
    return true
end

local obj2 = {};
setmetatable(obj2, myMetatable)

print(obj * obj2)       --输出 100
print(-obj)             --输出 -0.5
print(obj .. obj2)      --输出 -left.right-
print(obj == obj2)      --输出 true
print(obj > obj2)       --输出 true
print(obj >= obj2)      --输出 true

Lua对象

静态类

Person = {}
Person.id = 1
Person.name = "John"
Person.Greet = function() print("Hello") end
Person.Greet2 = function(caller) print("Hello! I`m " .. caller.name) end
function Person.Greet21(caller) print("Hello! I`m " .. caller.name) end
function Person:Greet3() print("Hello! I`m " .. self.name) end
Person.Greet()--Hello
Person.Greet2(Person)--Hello! I`m John
Person.Greet21(Person)--Hello! I`m John
Person:Greet3()--Hello! I`m John
  1. 类可以通过表来实现,可以通过属性赋值的方式给类添加成员变量和成员方法

  2. 成员方法的定义有2中3种方法:

  • 类名.方法名 = function(args) end

  • function 类名.方法名 (args) end

  • function 类名:方法名 () end

    其中第三种最为重要,在这种定义中,函数体内有一个self属性指向的是调用者本身。其他形式的定义中没有self属性。

普通类和实例化

通过自定义的new方法可以实现构造函数的效果

下面的new函数实现构造函数的步骤是:

  1. 声明初始化一个空表obj

  2. 将self(这里的self指向的就是调用者也就是类)作为元表,指定给这个obj

  3. 将self的__index指向self自身,这样obj就拥有了类上的属性和方法

Person = {}
Person.name = "John Wick"

function Person:new()
    local obj = {}
    setmetatable(obj, self)
    self.__index = self
    return obj
end

function Person:Greet() print("Hello ! I`m " .. self.name) end

local p1 = Person:new()
p1:Greet()--Hello ! I`m John Wick

local p2 = Person:new()
p2.name = "Dog"
p2:Greet()--Hello ! I`m Dog

local p3 = Person:new()
p3.name = "Gun"
p3:Greet()--Hello ! I`m Gun

Person:Greet()--Hello ! I`m John Wick

继承和多态

在上述示例中,增加subClass方法,即可实现继承

实现继承的步骤:

  1. 将子类注册到_G(全局变量)上

  2. 保存子类为局部变量

  3. self(也就是父类)作为子类的元表

  4. self__index指向self自己。此时子类拥有的父类的方法和属性

  5. 将子类的super指向self(也就是父类)。此时子类可以通过super属性访问到父类自己的方法(子类重写父类方法时,子类用来调用父类的方法)

function Person:subClass(className)
    _G[className]={}
    local class = _G[className]
    setmetatable(class,self)
    self.__index=self
    class.super=self
end

Person:subClass("Killer")
function Killer:Killing()
    print(self.name.." is performing mission")
end

function Killer:Greet()
    print("Killer Greet")
    self.super:Greet(self)
end
local p1=Killer:new()
p1:Greet()--Hello ! I`m John Wick
p1:Killing()--John Wick is performing mission

内置库

lua内置库提供了一些常用的处理方法,这里罗列的一部分,具体的方法自行查阅。

库名 功能 名称
string 字符串的处理 len,sub,gsub,upper,lower,find,match,format,dump…
table 表处理 setn,getn,insert,remove,sort,concat…
os 系统相关的处理 time,date…
math 数学方法 max,min,cos,sin,tan, pow, log, ceil, floor, random…
io 处理文件的读写

你可能感兴趣的:(Lua入门学习)