• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

Lua语法

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

为什么要学Lua呢,因为大学时玩了一款游戏叫饥荒,最近发现做脚本也要用到这门语言,于是就简单的学习一下,毕竟广大程序员的乐趣就是会敲各种语言的Hello World。

此篇参考链接:

http://www.runoob.com/lua/lua-tutorial.html

Lua官网

设计目的

其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。这句话言外之意就是“我就是个做脚本的,目的就是为了嵌入到应用程序中辅助扩展”。

Lua 特性

  • 轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
  • 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
  • 其它特性:
    • 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
    • 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象
    • 语言内置模式匹配;闭包(closure);函数也可以看做一个值提供多线程(协同进程,并非操作系统所支持的线程)支持;
    • 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。

Lua 应用场景

  • 游戏开发  http://love2d.org/
  • 独立应用脚本(这个就是我的目的,做游戏脚本)
  • Web 应用脚本
  • 扩展和数据库插件如:MySQL Proxy 和 MySQL WorkBench
  • 安全系统,如入侵检测系统

Lua 环境安装

Windows环境安装

google code下载,这个需要FQ,也可以从其他地方下载。

安装完后是个黑框框,cmd命令模式执行,跟java环境配置调用命令大同小异。这个是用来运行最终文件的。

跟java一样要有IDE,类似于IDEA和myeclipse,这个在安装Lua的过程中会有一个叫SciTE的软件,这个就是Lua的编辑器。

Linux安装环境

如果你没有Linux运行环境,仅仅是用Lua这么小的东西,不想安装虚拟机又不想买服务器的话。可以尝试腾讯云的Cloud Studio,在线编辑、运行。

Cloud Studio是基于浏览器的集成式开发环境,支持绝大部分编程语言,包括 HTML5、PHP、Python、Java、Ruby、C/C++、.NET 等等,无需下载安装程序,一键切换开发环境。Cloud Studio提供了完整的 Linux 环境,并且支持自定义域名指向,动态计算资源调整,可以完成各种应用的开发编译与部署。

新建界面时这个样子的,发现上面没有Lua,要去下载Code Runner插件。创建一个无来源的Blank,进入选择运行环境(这里我选择Java),然后安装Lua环境(效果和Windows安装的一样)

在java的linux上安装lua环境

sudo apt-get install libreadline-dev   #先安装lua环境需要的lib,不行的话换个环境试试
curl -R -O http://www.lua.org/ftp/lua-5.3.0.tar.gz
tar zxf lua-5.3.0.tar.gz
cd lua-5.3.0
make linux  
sudo make install  #一定要sudo,不然可能会出现权限不允许的情况,因为你是coding,文件夹是root

写个Hello World,没问题

如果你既不想装windows的环境也不想装linux的环境,仅仅是为了学习或者看Lua基础代码的运行结果,可以用lua官网自带的demo测试代码页面

Lua 基本语法

交互式编程

输入lua,进入控制台交互界面

脚本式编程

直接在cmd中输入lua main.lua,java是需要编译后再用java命令调用class文件,而lua不需要编译可以直接调用

注释

单行注释

两个减号是单行注释:

--

多行注释

--[[
 多行注释
 多行注释
 --]]

标示符

Lua 标示符用于定义一个变量,函数获取其他用户定义的项。标示符以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头上0个或多个字母,下划线,数字(0到9)。

最好不要使用下划线加大写字母的标示符,因为Lua的保留字也是这样的。Lua 不允许使用特殊字符如 @, $, 和 % 来定义标示符。 Lua 是一个区分大小写的编程语言。

关键词

变量

全局变量:

在默认情况下,变量总是认为是全局的。全局变量不需要声明,给一个变量赋值后即创建了这个全局变量,访问一个没有初始化的全局变量也不会出错,只不过得到的结果是:nil。如果你想删除一个全局变量,只需要将变量赋值为nil。

一句话总结:变量默认全局,为初始化为nil,删除全局变量赋值为nil(和C语言置为空指针类似)

Lua 变量有三种类型:全局变量、局部变量、表中的域。

Lua 中的变量全是全局变量,那怕是语句块或是函数里,除非用 local 显式声明为局部变量。局部变量的作用域为从声明位置开始到所在语句块结束。变量的默认值均为 nil。

Lua可以对多个变量同时赋值,变量列表和值列表的各个元素用逗号分开,赋值语句右边的值会依次赋给左边的变量。

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

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

当变量个数和值的个数不一致时,Lua会一直以变量个数为基础采取以下策略:

变量个数 > 值的个数             按变量个数补足nil
变量个数 < 值的个数             多余的值会被忽略

多值赋值经常用来交换变量,或将函数调用返回给变量(函数可以返回多个变量)

f()返回两个值,第一个赋给a,第二个赋给b。应该尽可能的使用局部变量,有两个好处:

  • 局部变量可以避免命名冲突。
  • 访问局部变量的速度比全局变量更快。

对 table 的索引使用方括号 []。Lua 也提供了 . 操作

--[[
全局变量与局部变量
]]--
local a ='' --局部变量作用于当前块(这里相当于脚本文件)
do
    local b ='喵喵喵'  --局部变量作用于当前块(do end块)
end

local f = function(param)
    print(a,param)
    c = function()   --全局变量作用于全局
    end
end

f('')
print(a,b,c,param)

Lua 数据类型

Lua是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。Lua中有8个基本类型分别为:nil、boolean、number、string、userdata、function、thread和table。

例:

nil

nil 类型表示一种没有任何有效值,它只有一个值 -- nil,例如打印一个没有赋值的变量,便会输出一个 nil 值,对于全局变量和 table,nil 还有一个"删除"作用,给全局变量或者 table 表里的变量赋一个 nil 值,等同于把它们删掉(类似于C的空指针)

nil 作比较时应该加上双引号 ",因为type函数返回的是一个字符串

boolean

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

number

Lua默认只有一种 number 类型 -- double(双精度)类型(默认类型可以修改 luaconf.h 里的定义)

string

一对单引号双引号方括号 "[[]]" 来表示"一块"字符串

 在对一个数字字符串上进行算术操作时,Lua 会尝试将这个数字字符串转成一个数字

字符串连接使用的是两个点 ..

使用 #计算字符串的长度,放在字符串前面

table

table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。也可以在表里添加一些数据(相当于数组),直接初始化表

Lua 中的表(table)其实是一个"关联数组"(associative arrays),数组的索引可以是数字或者是字符串(相当于map,叫做字典)。

a = {} -- 新建一个全局空表(这里用法可以理解为一个map)
a["key1"] = "value1"  -- key和value
key = 10  -- 定义key变量
a[key] = 22  -- 将变量key作为键,22作为值
a[key] = a[key] + 11  -- 根据键修改值
--[[遍历table]]--
for k, v in pairs(a) do  
    print(k .. " : " .. v)
end

结果如下:

不同于其他语言的数组把 0 作为数组的初始索引,在 Lua 里表的默认初始索引一般以 1 开始

local tbl = {"apple", "pear", "orange", "grape"}   -- 这个table看起来像数组
for key, val in pairs(tbl) do  -- 遍历它,key为table的下标
    print("Key", key)
end

table 不会固定长度大小,有新数据添加时 table 长度会自动增长,没初始的 table项都是 nil。

function

在 Lua 中,函数是被看作是"第一类值(First-Class Value)",函数可以存在变量里

function factorial1(n)  -- 阶乘算法函数
    if n == 0 then
        return 1
    else
        return n * factorial1(n - 1)
    end
end
print(factorial1(5))  --打印factorial1函数返回的值
factorial2 = factorial1  -- 把函数factorial1作为参数赋给全局变量factorial2
print(factorial2(5))   -- 调用函数factorial2

function可以以匿名函数(anonymous function)的方式作为参数传递

function testFun(tab,fun)    -- 遍历tab,并对每个key-val做fun函数处理
    for k ,v in pairs(tab) do
        print(fun(k,v));
    end
end

tab={key1="val1",key2="val2"};
-- 匿名函数作为参数
testFun(tab, --第一个参数为表
function(key,val)  --匿名函数作为第二个参数
    return key.."="..val;  --匿名参数实现内容
end
);

function调用方式

. 方式赋值函数human更像作为表的一个属性,这个属性是function,参数是需要自己传的。

: 方式声明的函数可以通过self.的方式调用表对象的其他参数。

-- 函数调用方式
human = {};
human.money = 1000;
-- .方式
human.isRich1 = function(h)
    return h.money>500;
end
-- :方式,self就代表函数所属的table
function human:isRich2()
    return self.money>500;
end

print(human.isRich1(human));
print(human:isRich2());

thread

在 Lua 里,最主要的线程是协同程序(coroutine)。它跟线程(thread)差不多,拥有自己独立的栈、局部变量和指令指针,可以跟其他协同程序共享全局变量和其他大部分东西。

线程跟协程的区别:线程可以同时多个运行,而协程任意时刻只能运行一个,并且处于运行状态的协程只有被挂起(suspend)时才会暂停。

userdata

userdata 是一种用户自定义数据,用于表示一种由应用程序或 C/C++ 语言库所创建的类型,可以将任意 C/C++ 的任意数据类型的数据(通常是 struct 和 指针)存储到 Lua 变量中调用。

Lua 流程控制

布尔表达式,除了nil和false为假其余全部为真。取反用not

if(布尔表达式 1)
then
   --[ 在布尔表达式 1 为 true 时执行该语句块 --]
elseif( 布尔表达式 2)
then
   --[ 在布尔表达式 2 为 true 时执行该语句块 --]
else 
   --[ 如果以上布尔表达式都不为 true 则执行该语句块 --]
end

Lua 循环

while

statements(循环体语句) 可以是一条或多条语句,condition(条件) 可以是任意表达式,在 condition(条件) 为 true 时执行循环体语句。

while(condition)
do
   statements
end

repeat...until

在条件进行判断前循环体都会执行一次。如果条件判断语句(condition)为 false,循环会重新开始执行,直到条件判断语句(condition)为 true 才会停止执行。

repeat
   statements
until( condition )

for

var 从 exp1 变化到 exp2,每次变化以 exp3 为步长递增 var,并执行一次 "执行体"。exp3 是可选的,如果不指定,默认为1。

for var=exp1,exp2,exp3 do  
    <执行体>  
end  

泛型for

遍历数组,i是数组索引值,v是对应索引的数组元素值。ipairs是Lua提供的一个迭代器函数,用来迭代数组。

--打印数组a的所有值  
a = {"one", "two", "three"}
for i, v in ipairs(a) do
    print(i, v)
end 

遍历字典,k是key,v是value。

-- 遍历字典t的所有值,key一定为string所以不用加引号,value不一定为string所以要加
t = {name="Young",age = 23,sex = "girl"}
for k, v in pairs(t) do
    print(k, v, t[k])
end 

循环实例

--[[循环]]--
local a = 0
-- while死循环,每0.5秒执行一次,不影响性能
while wait(0.5) do 
    print(a)
    a =a+1
end
print('print loop end')

-- repeat循环
repeat 
    print(a)
    a = a+1
until a>10 

-- for循环三个参数分别为起止值,步长
for i=1,10,1 do
    print(i)
end

--遍历数组
local t = {"Monday", "Tuesday", "Wednesday","Thursday","Friday","Saturday","Sunday"}
t[1]="啦啦啦"  --更改数组第一位的值
print(t[1])
for i, v in ipairs(t) do
    print(i, v,t[i])
end 

-- 遍历字典(key=value)
local t2 = {Monday=1, Tuesday=2, Wednesday=3,Thursday=4,Friday=5,Saturday=6,Sunday=7}
t2["Monday"]="啦啦啦~"  -- 修改key为Monday的键值对的值为啦啦啦~
print(t2['Monday'])  -- 打印键为Monday的键值对的值
for i, v in pairs(t2) do
    print(i, v,t2[i])
end 

Lua 函数

Lua 函数主要有两种用途:

  • 1.完成指定的任务,这种情况下函数作为调用语句使用;
  • 2.计算并返回值,这种情况下函数作为赋值语句的表达式使用。
function_scope function function_name( argument1, argument2, argument3..., argumentn)
    function_body
    return result_params_comma_separated
end

解析:

  • function_scope: 该参数是可选的制定函数是全局函数还是局部函数,未设置该参数默认为全局函数,如果你需要设置函数为局部函数需要使用关键字 local

  • function_name: 指定函数名称。

  • argument1, argument2, argument3..., argumentn: 函数参数,多个参数以逗号隔开,函数也可以不带参数。

  • function_body: 函数体,函数中需要执行的代码语句块。

  • result_params_comma_separated: 函数返回值,Lua语言函数可以返回多个值,每个值以逗号隔开。

多返回值

Lua函数可以返回多个结果值,比如string.find,其返回匹配串"开始和结束的下标"(如果不存在匹配串返回nil)。

s, e = string.find("www.runoob.com", "runoob") 
print(s, e)

方法内写法

function maximum (a)  -- 返回table a的最大值和索引
    local mi = 1             -- 最大值索引,初始为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}))

可变参数

Lua 函数可以接受可变数目的参数,和 C 语言类似,在函数参数列表中使用三点 ... 表示函数有可变的参数。

function average(...)   -- 取平均值
   result = 0   -- 结果
   local arg_tbl ={...}    --> arg 为一个表,局部变量,将输入的过个参数封装为一个表
   for i,v in ipairs(arg_tbl) do  -- 遍历表
      result = result + v    -- 将value相加
   end  
   print("总共传入 " .. #arg .. " 个数")
   return result/#arg_tbl -- 将综合除以总数量
end

print("平均值为",average(10,5,3,4,5,6))
  • select('#', …) 返回可变参数的长度
  • select(n, …) 用于访问 n 到 select('#',…) 的参数

运算符

运算符是一个特殊的符号,用于告诉解释器执行特定的数学或逻辑运算。Lua提供了以下几种运算符类型:

  • 算术运算符

  • 关系运算符

  • 逻辑运算符

  • 其他运算符

运算符优先级

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

字符串

字符串或串(String)是由数字、字母、下划线组成的一串字符。

Lua 语言中字符串可以使用以下三种方式来表示:

  • 单引号间的一串字符。
  • 双引号间的一串字符。
  • [[和]]间的一串字符。

转义字符

字符串操作

string.upper(argument)  -- 字符串全部转为大写字母
string.lower(argument)  -- 字符串全部转为小写字母
string.gsub(mainString,findString,replaceString,num)   -- 在字符串中替换,mainString为要替换的字符串, findString 为被替换的字符,replaceString 要替换的字符,num 替换次数(可以忽略,则全部替换)
string.find (str, substr, [init, [end]])  --在一个指定的目标字符串中搜索指定的内容(第三个参数为索引),返回其具体位置。不存在则返回 nil。
string.reverse(arg)  -- 字符串反转
string.format(...)  --  返回一个类似printf的格式化字符串
string.char(arg) 和 string.byte(arg[,int])  --  char 将整型数字转成字符并连接, byte 转换字符为整数值(可以指定某个字符,默认第一个字符)。
string.len(arg)  --  计算字符串长度。
string.rep(string, n)  -- 返回字符串string的n个拷贝
..  -- 链接两个字符串
string.gmatch(str, pattern)  --  回一个迭代器函数,每一次调用这个函数,返回一个在字符串 str 找到的下一个符合 pattern 描述的子串。如果参数 pattern 描述的字符串没有找到,迭代函数返回nil。
string.match(str, pattern, init)  --  string.match()只寻找源字串str中的第一个配对. 参数init可选, 指定搜寻过程的起点, 默认为1。 
在成功配对时, 函数将返回配对表达式中的所有捕获结果; 如果没有设置捕获标记, 则返回整个配对字符串. 当没有成功的配对时, 返回nil。

string.format字符串格式化

pattern匹配模式

数组

数组,就是相同数据类型的元素按一定顺序排列的集合,可以是一维数组和多维数组。Lua 数组的索引键值可以使用整数表示,数组的大小不是固定的。

一维数组

一维数组是最简单的数组,其逻辑结构是线性表。一维数组可以用for循环出数组中的元素

array = {"Lua", "Tutorial"}

for i= 0, 2 do
   print(array[i])
end

多维数组

多维数组即数组中包含数组或一维数组的索引键对应一个数组。以下是一个三行三列的阵列多维数组

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

Lua 迭代器

泛型 for 迭代器

泛型 for 在自己内部保存迭代函数,实际上它保存三个值:迭代函数、状态常量、控制变量。泛型 for 迭代器提供了集合的 key/value 对,语法格式如下:

for k, v in pairs(t) 
do
    print(k, v)
end

无状态的迭代器

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

function square(iteratorMaxCount,currentNumber)
   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
   return currentNumber, currentNumber*currentNumber
   end
end

for i,n in square,3,0
do
   print(i,n)
end

table表

Lua也是通过table来解决模块(module)、包(package)和对象(Object)的。 例如string.format表示使用"format"来索引table string。

table操作

获取table长度,当我们获取 table 的长度的时候无论是使用 # 还是 table.getn 其都会在索引中断的地方停止计数,而导致无法正确取得 table 的长度。

可以使用以下方法来代替:

function table_leng(t)
  local leng=0
  for k, v in pairs(t) do
    leng=leng+1
  end
  return leng;
end

模块与包

模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。

Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:

-- 文件名为 module.lua
-- 定义一个名为 module 的模块(其实就是一个全局变量)
module = {} 
 
-- 定义一个常量
module.constant = "这是一个常量"
 
-- 定义一个函数
function module.func1()
    io.write("这是一个公有函数!\n")
end
 
local function func2()
    print("这是一个私有函数!")
end
 
function module.func3()
    func2()
end
 
return module -- 返回对象(把开始定义的那个全局变量在最末尾处返回)

require 函数

require("<模块名>")  --写法一
require "<模块名>"  --写法二

用法一,全局要用

-- main.lua 文件
-- module 模块为上文提到到 module.lua
require("module")  -- 在最上面声明,相当于java的import
 
print(module.constant)  -- module.lua文件中声明的全局变量名module.变量名
 
module.func3()  --  module.lua文件中声明的全局变量名module.函数名

用法二,局部要用

-- main2.lua 文件
-- module 模块为上文提到到 module.lua
-- 别名变量 m
local m = require("module")  -- 把require函数返回module.lua文件中返回的全局变量module并赋值给本地变量m
 
print(m.constant)
 
m.func3()

加载机制

对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。

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

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

#LUA_PATH
export LUA_PATH="~/lua/?.lua;;"

文件路径以 ";" 号分隔,最后的 2 个 ";;" 表示新加的路径后面加上原来的默认路径。

接着,更新环境变量参数,使之立即生效。

source ~/.profile

这时假设 package.path 的值是:

/Users/dengjoe/lua/?.lua;./?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?/init.lua

那么调用 require("module") 时就会尝试打开以下文件目录去搜索目标。

/Users/dengjoe/lua/module.lua;
./module.lua
/usr/local/share/lua/5.1/module.lua
/usr/local/share/lua/5.1/module/init.lua
/usr/local/lib/lua/5.1/module.lua
/usr/local/lib/lua/5.1/module/init.lua

如果找过目标文件,则会调用 package.loadfile 来加载模块。否则,就会去找 C 程序库。

搜索的文件路径是从全局变量 package.cpath 获取,而这个变量则是通过环境变量 LUA_CPATH 来初始。

搜索的策略跟上面的一样,只不过现在换成搜索的是 so 或 dll 类型的文件。如果找得到,那么 require 就会通过 package.loadlib 来加载它。

C包

该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
[转][译]ClosuresinLua-Lua中的闭包发布时间:2022-07-22
下一篇:
Cocos2d-lua 初识shader之四:描边发布时间:2022-07-22
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap