lua基础

2018-06-13  本文已影响168人  passiony
lua

Lua 5.3 参考手册

http://www.runoob.com/manual/lua53doc/manual.html

Lua

Lua 是一门扩展式程序设计语言,被设计成支持通用过程式编程,并有相关数据描述设施。 同时对面向对象编程、函数式编程和数据驱动式编程也提供了良好的支持。 它作为一个强大、轻量的嵌入式脚本语言,可供任何需要的程序使用。 Lua 由 clean C(标准 C 和 C++ 间共通的子集) 实现成一个库。

作为一门扩展式语言,Lua 没有 "main" 程序的概念: 它只能 嵌入 一个宿主程序中工作, 该宿主程序被称为 被嵌入程序 或者简称 宿主 。 宿主程序可以调用函数执行一小段 Lua 代码,可以读写 Lua 变量,可以注册 C 函数让 Lua 代码调用。 依靠 C 函数,Lua 可以共享相同的语法框架来定制编程语言,从而适用不同的领域。 Lua 的官方发布版包含一个叫做 lua 的宿主程序示例, 它是一个利用 Lua 库实现的完整独立的 Lua 解释器,可用于交互式应用或批处理。

基本数据类型

Lua 是一门动态类型语言。 这意味着变量没有类型;只有值才有类型。 语言中不设类型定义。 所有的值携带自己的类型。
Lua 中有八种基本类型: nil、boolean、number、string、function、userdata、 thread 和 table。

String

这个库提供了字符串处理的通用函数。 例如字符串查找、子串、模式匹配等。 当在 Lua 中对字符串做索引时,第一个字符从 1 开始计算(而不是 C 里的 0 )。 索引可以是负数,它指从字符串末尾反向解析。 即,最后一个字符在 -1 位置处,等等。
字符串库中的所有函数都在表 string 中。 它还将其设置为字符串元表的 __index 域。 因此,你可以以面向对象的形式使用字符串函数。 例如,string.byte(s,i) 可以写成 s:byte(i)

string.byte (s [, i [, j]])

返回字符 s[i]s[i+1], ... ,s[j] 的内部数字编码。 i 的默认值是 1 ; j 的默认值是 i。 这些索引以函数 string.sub 的规则修正。

string.char (···)

接收零或更多的整数。 返回和参数数量相同长度的字符串。 其中每个字符的内部编码值等于对应的参数值。

string.dump (function [, strip])

返回包含有以二进制方式表示的(一个 二进制代码块 )指定函数的字符串。 之后可以用 load 调用这个字符串获得 该函数的副本(但是绑定新的上值)。 如果 strip 为真值, 二进制代码块不携带该函数的调试信息 (局部变量名,行号,等等。)。

string.find (s, pattern [, init [, plain]])

查找第一个字符串 s 中匹配到的 pattern (参见 §6.4.1)。 如果找到一个匹配,find 会返回 s 中关于它起始及终点位置的索引; 否则,返回 nil。 第三个可选数字参数 init 指明从哪里开始搜索; 默认值为 1 ,同时可以是负值。 第四个可选参数 plaintrue 时, 关闭模式匹配机制。 此时函数仅做直接的 “查找子串”的操作, 而 pattern 中没有字符被看作魔法字符。 注意,如果给定了 plain ,就必须写上 init

string.format (formatstring, ···)

返回不定数量参数的格式化版本, 格式化串为第一个参数(必须是一个字符串)。 格式化字符串遵循 ISO C 函数 sprintf 的规则。 不同点在于选项 *, h, L, l, n, p 不支持, 另外还增加了一个选项 qq 选项将一个字符串格式化为两个双引号括起,对内部字符做恰当的转义处理的字符串。 该字符串可以安全的被 Lua 解释器读回来。
例如:
选项 A and a (如果有的话), E, e, f, G, and g 都期待一个对应的数字参数。 选项 c, d, i, o, u, X, and x 则期待一个整数。 选项 q 期待一个字符串; 选项 s 期待一个没有内嵌零的字符串。 如果选项 s 对应的参数不是字符串,它会用和 tostring 一致的规则转换成字符串。

string.gmatch (s, pattern)

返回一个迭代器函数。 每次调用这个函数都会继续以 pattern (参见 §6.4.1) 对 s 做匹配,并返回所有捕获到的值。 如果 pattern 中没有指定捕获,则每次捕获整个 pattern

下面这个例子会循环迭代字符串 s 中所有的单词, 并逐行打印:

    s = "hello world from Lua"
     for w in string.gmatch(s, "%a+") do
       print(w)
     end

下一个例子从指定的字符串中收集所有的键值对 key=value 置入一张表:

     t = {}
     s = "from=world, to=Lua"
     for k, v in string.gmatch(s, "(%w+)=(%w+)") do
       t[k] = v
     end

string.gsub (s, pattern, repl [, n])

将字符串 s 中,所有的(或是在 n 给出时的前 n 个) pattern (参见 §6.4.1)都替换成 repl ,并返回其副本。 repl 可以是字符串、表、或函数。 gsub 还会在第二个返回值返回一共发生了多少次匹配。 gsub 这个名字来源于 Global SUBstitution

如果 repl 是一个字符串,那么把这个字符串作为替换品。 字符 % 是一个转义符: repl 中的所有形式为 %*d* 的串表示 第 d 个捕获到的子串,d 可以是 1 到 9 。 串 %0 表示整个匹配。 串 %% 表示单个 %

如果 repl 是张表,每次匹配时都会用第一个捕获物作为键去查这张表。

如果 repl 是个函数,则在每次匹配发生时都会调用这个函数。 所有捕获到的子串依次作为参数传入。

任何情况下,模板中没有设定捕获都看成是捕获整个模板。

如果表的查询结果或函数的返回结果是一个字符串或是个数字, 都将其作为替换用串; 而在返回 falsenil 时不作替换 (即保留匹配前的原始串)。

这里有一些用例:

    x = string.gsub("hello world", "(%w+)", "%1 %1")
     --> x="hello hello world world"

     x = string.gsub("hello world", "%w+", "%0 %0", 1)
     --> x="hello hello world"

     x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
     --> x="world hello Lua from"

     x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
     --> x="home = /home/roberto, user = roberto"

     x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
           return load(s)()
         end)
     --> x="4+5 = 9"

     local t = {name="lua", version="5.3"}
     x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
     --> x="lua-5.3.tar.gz"

string.len (s)

接收一个字符串,返回其长度。 空串 "" 的长度为 0 。 内嵌零也统计在内,因此 "a\000bc\000" 的长度为 5 。

string.lower (s)

接收一个字符串,将其中的大写字符都转为小写后返回其副本。 其它的字符串不会更改。 对大写字符的定义取决于当前的区域设置。

string.match (s, pattern [, init])

在字符串 s 中找到第一个能用 pattern (参见 §6.4.1)匹配到的部分。 如果能找到,match 返回其中的捕获物; 否则返回 nil 。 如果 pattern 中未指定捕获, 返回整个 pattern 捕获到的串。 第三个可选数字参数 init 指明从哪里开始搜索; 它默认为 1 且可以是负数。

string.pack (fmt, v1, v2, ···)

返回一个打包了(即以二进制形式序列化) v1, v2 等值的二进制字符串。 字符串 fmt 为打包格式(参见 §6.4.2)。

string.packsize (fmt)

返回以指定格式用 string.pack 打包的字符串的长度。 格式化字符串中不可以有变长选项 's' 或 'z' (参见 §6.4.2)。

string.rep (s, n [, sep])

返回 n 个字符串 s 以字符串 sep 为分割符连在一起的字符串。 默认的 sep 值为空字符串(即没有分割符)。 如果 n 不是正数则返回空串。

string.reverse (s)

返回字符串 s 的翻转串。

string.sub (s, i [, j])

返回 s 的子串, 该子串从 i 开始到 j 为止; ij 都可以为负数。 如果不给出 j ,就当它是 -1 (和字符串长度相同)。 特别是, 调用 string.sub(s,1,j) 可以返回 s 的长度为 j 的前缀串, 而 string.sub(s, -i) 返回长度为 i 的后缀串。

如果在对负数索引转义后 i 小于 1 的话,就修正回 1 。 如果 j 比字符串的长度还大,就修正为字符串长度。 如果在修正之后,i 大于 j, 函数返回空串。

string.unpack (fmt, s [, pos])

返回以格式 fmt (参见 §6.4.2) 打包在字符串 s (参见 string.pack) 中的值。 选项 pos(默认为 1 )标记了从 s 中哪里开始读起。 读完所有的值后,函数返回 s 中第一个未读字节的位置。

string.upper (s)

接收一个字符串,将其中的小写字符都转为大写后返回其副本。 其它的字符串不会更改。 对小写字符的定义取决于当前的区域设置。

6.4.1 – 匹配模式

Lua 中的匹配模式直接用常规的字符串来描述。 它用于模式匹配函数 string.find, string.gmatch, string.gsub, string.match。 这一节表述了这些字符串的语法及含义(即它能匹配到什么)。

字符类:

字符类 用于表示一个字符集合。 下列组合可用于字符类:

所有单个字母表示的类别(%a%c,等), 若将其字母改为大写,均表示对应的补集。 例如,%S 表示所有非空格的字符。

如何定义字母、空格、或是其他字符组取决于当前的区域设置。 特别注意:[a-z] 未必等价于 %l

模式条目:

模式条目 可以是

模式:

模式 指一个模式条目的序列。 在模式最前面加上符号 '^' 将锚定从字符串的开始处做匹配。 在模式最后面加上符号 '$' 将使匹配过程锚定到字符串的结尾。 如果 '^' 和 '$' 出现在其它位置,它们均没有特殊含义,只表示自身。

捕获:

模式可以在内部用小括号括起一个子模式; 这些子模式被称为 捕获物。 当匹配成功时,由 捕获物 匹配到的字符串中的子串被保存起来用于未来的用途。 捕获物以它们左括号的次序来编号。 例如,对于模式 "(a*(.)%w(%s*))" , 字符串中匹配到 "a*(.)%w(%s*)" 的部分保存在第一个捕获物中 (因此是编号 1 ); 由 "." 匹配到的字符是 2 号捕获物, 匹配到 "%s*" 的那部分是 3 号。

作为一个特例,空的捕获 () 将捕获到当前字符串的位置(它是一个数字)。 例如,如果将模式 "()aa()" 作用到字符串 "flaaap" 上,将产生两个捕获物: 3 和 5 。

Table

这个库提供了表处理的通用函数。 所有函数都放在表 table 中。

记住,无论何时,若一个操作需要取表的长度, 这张表必须是一个真序列,或是拥有 __len 元方法 (参见 §3.4.7 )。 所有的函数都忽略传入参数的那张表中的非数字键。

table.concat (list [, sep [, i [, j]]])

提供一个列表,其所有元素都是字符串或数字,返回字符串 list[i]..sep..list[i+1] ··· sep..list[j]sep 的默认值是空串, i 的默认值是 1 , j 的默认值是 #list 。 如果 ij 大,返回空串。

table.insert (list, [pos,] value)

list 的位置 pos 处插入元素 value , 并后移元素 list[pos], list[pos+1], ···, list[#list]pos 的默认值为 #list+1 , 因此调用 table.insert(t,x) 会将 x 插在列表 t 的末尾。

table.move (a1, f, e, t [,a2])

将元素从表 a1 移到表 a2。 这个函数做了次等价于后面这个多重赋值的等价操作: a2[t],··· = a1[f],···,a1[e]a2 的默认值为 a1。 目标区间可以和源区间重叠。 索引 f 必须是正数。

table.pack (···)

返回用所有参数以键 1,2, 等填充的新表, 并将 "n" 这个域设为参数的总数。 注意这张返回的表不一定是一个序列。

table.remove (list [, pos])

移除 listpos 位置上的元素,并返回这个被移除的值。 当 pos 是在 1 到 #list 之间的整数时, 它向前移动元素 list[pos+1], list[pos+2], ···, list[#list] 并删除元素 list[#list]; 索引 pos 可以是 #list + 1 ,或在 #list 为 0 时可以是 0 ; 在这些情况下,函数删除元素 list[pos]

pos 默认为 #list, 因此调用 table.remove(l) 将移除表 l 的最后一个元素。

table.sort (list [, comp])

在表内从 list[1]list[#list] 原地 对其间元素按指定次序排序。 如果提供了 comp , 它必须是一个可以接收两个列表内元素为参数的函数。 当第一个元素需要排在第二个元素之前时,返回真 (因此 not comp(list[i+1],list[i]) 在排序结束后将为真)。 如果没有提供 comp, 将使用标准 Lua 操作 < 作为替代品。

排序算法并不稳定; 即当两个元素次序相等时,它们在排序后的相对位置可能会改变。

table.unpack (list [, i [, j]])

返回列表中的元素。 这个函数等价于 return list[i], list[i+1], ···, list[j] ; i 默认为 1 ,j 默认为 #list

lua 面向对象

LUA作为一种世界上广泛使用的游戏脚本语言,有其强大的一面。现在的游戏脚本,基本上都基于面向对象了,因为非面向对象的语言写游戏这种复杂的脚本明显过于麻烦。而LUA不是面向对象的语言,但是为什么会有这么多游戏使用呢?因为LUA之中有强大的table,这个类极为强大,可以当做数组,对象,类,哈希表总之什么都是。。。并且LUA之中具有metatable“元表”这个概念,所以你还可以使用table来构建其他语言的各种数据结构,因为这只是限制了table的功能而已,所以lua的强悍之处在于,它是一种可以自定义数据结构行为的语言,

1,类
类一般都有类名,属性声明,构造体,方法,属性。下面用LUA实现类的模拟,类的模拟有很多种,但是都必须用到__index。这里为了方便只是用一种定式,并且soyomaker的脚本以后也会采用这种定式。

--声明,这里声明了类名还有属性,并且给出了属性的初始值。
Class = {x=0,y=0}
--这句是重定义元表的索引,就是说有了这句,这个才是一个类。具体的解释,请百度。
Class.__index = Class
--构造体,构造体的名字是随便起的,习惯性改为new()
function Class:new(x,y)
        local self = {}  --初始化self,如果没有这句,那么类所建立的对象改变,其他对象都会改变
        setmetatable(self, Class)  --将self的元表设定为Class
        self.x = x   --属性值初始化
        self.y = y
        return self  --返回自身
end
--这里定义类的其他方法
function Class:test()
    print(self.x,self.y)
end
function Class:plus()
    self.x = self.x + 1
    self.y = self.y + 1
end

好了一个类定义完毕。现在我们可以开始建立对象了。

a = Class:new(10,20)
a:test()
b = Class:new(11,23)
b:test()
b:plus()
b:test()

结果是
10 20
11 23
12 24

看,两个对象a和b互不影响,并且都可以使用Class的方法,这就是一个类了。
2,继承
LUA之中没有继承的概念,但是可以模拟,我们建立一个新的类Main,它是Class类的子类,包含其一切方法。

--声明了新的属性Z
Main = {z=0}
--设置类型是Class
setmetatable(Main, Class)
--还是和类定义一样,表索引设定为自身
Main.__index = Main
--这里是构造体,看,加上了一个新的参数
function Main:new(x,y,z)
   local self = {}  --初始化对象自身
   self = Class:new(x,y) --将对象自身设定为父类,这个语句相当于其他语言的super
   setmetatable(self, Main) --将对象自身元表设定为Main类
   self.z= z --新的属性初始化,如果没有将会按照声明=0
   return self
end
--定义一个新的方法
function Main:go()
   self.x = self.x + 10
end
--重定义父类的方法
function Main:test()
    print(self.x,self.y,self.z)
end

好了然后我们建立对象

c = Main:new(20,40,100)
c:test()
d = Main:new(10,50,200)
d:go()
d:plus()
d:test()
c:test()

运行,结果是
20 40 100
21 51 200
20 40 100

明显,他可以运行父类的方法,并且可以重载父类的方法。所以Main继承了Class,为其子类。
3,多态
LUA作为动态语言,支持多态是基本,但是对象呢,这里我们加个方法证明这一点。

Class = {x=0,y=0}
Class.__index = Class
function Class:new(x,y)
        local self = {}
        setmetatable(self, Class)
        self.x = x
        self.y = y
                     return self
end
function Class:test()
    print(self.x,self.y)
end
--新定义的一个函数gto()
function Class:gto()
   return 100
end
--这里会引用gto()
function Class:gio()
   return self:gto() * 2
end
function Class:plus()
    self.x = self.x + 1
        self.y = self.y + 1
end

然后在main哪里加入新的定义。

Main = {z=0}
setmetatable(Main, Class)
Main.__index = Main
function Main:new(x,y,z)
   local self = {}
   self = Class:new(x,y)
   setmetatable(self, Main)
   self.z= z
   return self
end
--重新定义了gto()
function Main:gto()
   return 50
end
function Main:go()
   self.x = self.x + 10
end
function Main:test()
    print(self.x,self.y,self.z)
end

然后我们开始测试

a = Class:new(10,20)
print(a:gio())
d = Main:new(10,50,200)
print(d:gio())
print(a:gio())

结果是
200
100
200

可见其实LUA也可以使用多态的。
教程讲到这里,这三个东西是游戏脚本上面基本上最为重要的东西,完全证明了,LUA一样可以构建一个完整的游戏脚本,并且因为LUA的特性而言,只会比JAVA这些更加简单而不会更加困难,另外就是LUA语言就是开发来嵌入其他语言中使用的,所以游戏中使用LUA作为脚本是绝对明智的选择。

上一篇 下一篇

猜你喜欢

热点阅读