Lua学习笔记-语法

前言

Lua语言是一种嵌入式语言被广泛的用于各个领域,而且与C语言的兼容性比较好.Openresty当中就可以使用Lua进行快速开发.Redis可以借助Lua实现事务.以及各大游戏引擎有很多都支持Lua脚本.以此可见Lua使用范围是很广泛的.
本篇将Lua语言和其它几种语言(PHP,JavaScript,Go,Shell,C)做对比,进行对比的记忆.
Lua的语法风格和shell语言有些相似之处,但不完全相同.
学习多种语言要比较的去记忆,防止弄混,否则可能最后学成一门四不像语言.

变量

变量的声明很简单,命名规则和C语言无差别.
字母或下划线开头,由字母下划线数字组成.

默认声明的变量为全局变量
如果需要局部变量,必须显示的声明local.

变量的作用域就是为了防止出现变量的污染而产生不可调和的错误.

JavaScript就有臭名昭著的变量作用域提升.(JavaScript的作用域分为全局作用域与函数作用域)导致不可调和的错误.
PHP中变量的作用域更是不清晰,列入for循环里定义的变量跳出了for循环还可以被访问.这个可能会造成一些错误.

a = 10

local b =10


局部变量的作用域:
声明变量的起始位置到所在语句块结束的位置.
语句块结束位置一般为if ,do,function等结束位置.

流程控制

Lua中的流程控制和shell语言写法相似,但是条件是用圆括号包起来,还有语句结束是用end,这点和shell的fi,done,esac等语句结束符不一样.

if

if语句条件结束之后加then标志符合条件内容执行范围,其实本质是和{一个意思.
elseif是关键字,不能使用else if否则会导致错误,在一些语言当中如PHP是两种语法都支持(但书写规范是用elseif).

if (a ~= 40 ) then
    print(a)
elseif (a > 10) then 
    print(a)
end

while


while (a < 10) then
    a--
end

repeat

Lua语言当中的repeat和其他语言当中的do while是一样的.只不过是名字起得奇怪点.
实际编程项目当中do while 一般用的少.

repeat 
    print(a)
until (a<10) 

for

for var=expr,dest,step do

end

lua中的for表达式比较特别,var=expr进行赋值,dest表是var要变更到的目标值,step表是步长.
如果step变量缺失默认步长为1.


for i,v in ipairs(a) do
   
end

注释

Lua当中的注释有些别扭.
单行注释以 --开头.
多行注释很奇怪:

--[[
 注释
 注释
 --]]

数据类型

string,number,function,boolean,nil,userdata,table,thread.

function

函数的声明:

function test(...)
    return 1, 2, 3
end
a, b, c = test({1,2,3}) -- a b c分别为1, 2, 3

函数特点:

  • 可以返回多个值,赋值的方式和Go语言有些相似,可能Go语言灵感来源于此?一般把第二个参数作为错误信息判断是否调用出错
  • 函数支持可变参数...,可变参数的本质是table.
  • 函数也有对应的作用域,可以和变量一样声明为local作用范围为定义的文件内,默认为全局函数.

Lua中支持匿名函数,可以将匿名函数赋值给变量.

字符串

Lua当中的字符串既可以用单引号也可以用双引号声明字符串.


a = '123\'"' --123'"
b = "123'\"" --123'"

唯一的区别是单引号字符串需要转义单引号,双引号字符串需要转义单引号

Lua不能支持字符串和数字比较,因为这可能会引起歧义.字符串的比较的是ASCII顺序值,数字比较大小.
在其他解释形语言当中支持这种操作,一般是把字符串转换位数字,例如提供了=====这两种操作符.
特别的如果将数字和一个不能转换为数字的字符串进行比较,会产生严重的歧义.PHP会把不能转换为数字的字符串的值作为false也就是0值再和数字进行比较.JS中这种情况永远不会反会true.

字符串声明支持多行字符串.

a = [[
    123
    456
    789
]]

这个和PHP以及Shell当中使用的heredoc有些类似.
如果字符串内出现了[[,需要在字符串起始的两个方括号之间加入等号,这样就可以包含[[.

a = [==[
    123
    456
    789
    [[
]==]
输出
    123
    456
    789
    [[

字符串的拼接使用的是..操作符.

table

Lua中的数组和PHP中的数组概念上很相似,而且都可以支持传统意义上的数组(也叫列表)也支持关联数组.
但是有一点数组的下标从1开始. 这点和其它语言不一致.

表永远是匿名的,目的是为了垃圾回收机制.
使用 #a 可以获取列表a的长度,无法获取真正的数组a的长度.

table constructor

表构造器是指用于创建一个表.
这点和PHP当中不一样,PHP当中使用数组时直接创建使用即可,不需要使用构造器进行构造.

也就是说下述代码会在Lua当中产生错误.

a[1] = 100 --错误
--使用之前必须要调用构造器
a = {} -- 空构造器
a = {1, 2, 3} --非空构造
a= {x = 10, y = 20}
a = {["x"] = 10, ["y"] = 20}

引用

a = {1, "abc"}
b = a

a[1] --1
b[1]=10 

-- a[1] = 10

引用机制和PHP中的数组不一样,PHP中的数组是用了copy on write机制.当对引用的数组修改时不对原数组产生影响而是新copy一份.

遍历

对于关联数组而言可用pairs进行遍历

t = {10, x=12, k = "123"}
for k,v pairs(t) do
    print(k,v)
end

对于列表而言可用ipairs进行遍历

t = {10, 12, "123"}
for i,v ipairs(t) do
    print(i,v)
end

数组长度

如何正确获取数组长度,#只能获取列表的长度.
前例当中的t,#t实际输出是1.但与实际长度不符合.
#等价于table.getn因此二者返回结果一致.

t = {10, x=12, k = "123"}
--#t  -> 1

元表

原表的概念和PHP当中类的魔术方法大同小异.JavaScript也有类似的概念.
通过原表可以实现继承.
通过元表当中的一些函数,改变表的属性等.

使用方式:

t = {}
metat = {}
setmetatable(t, metat)

__index

这与PHP当中的__get方法,当访问一个table不存在的键值时会返回原表当中指定的数据或者函数调用.

metat = {__index = 
    function (table,key)
        return key
    end
}
metat = {
    __index = { t = "123"}
}

__newindex

设置表中不存在的元素,会调用这个函数.

metat = {__newindex = 
    function (table,key,val)
        rawset(table, key, val .. "suffix") --rawset 函数直接对表赋值,跳过元表的__newindex操作,防止产生死循环
    end
}

除了上述方法之外还有__call,__tostring方法.

包管理

定义一个文件可以其中包含可以导出的变量一以及函数.


--module.lua
module = {}

function module.e()
    print(1)
end
--test.lua

reuqire("module")

module.e()

包的导入依赖环境变量LUA_PATH.

export LUA_PATH="~/?.lua;;"#  ;;是添加默认路径

可以打印package.path变量查看当前的默认导入路径.

面向对象

Lua中的面向对象是基于table实现.
其实这样也有些误导,因为Lua本来就没有面向对象的概念.就如Go当中一样,Go语言当中结构体的方法有类似的概念.
:操作符的函数,主要表示这个函数内可以访问self关键字.



 function Account:new (o)
      o = o or {}   -- create object if user does not provide one
      setmetatable(o, self)
      self.__index = self
      return o
    end

继承示例

A = {a = 10}

function A:new(o)
    o = o or {}
    self.__index = self --找不到的方法在self中找
    setmetatable(o, self) -- self 具有new方法
    return o
end


B = A:new({b = 20}) -- B继承了A的属性a,A的方法new,并有了新的属性b

C = B:new({c = 20}) --调用new方法时self指的是'B'这个类. 
instanceC = C:new()
--[[

C类的结构看起来是这样的:

{
    c = 20,
    metatable = {
        __index = {
            b = 20 
            metatable = {
                __index = {
                    a = 10
                }
            }
        }
    }

}
instanceC   
{
    metatable = {
    __index = {
        c = 20,
    metatable = {
        __index = {
            b = 20 
            metatable = {
                __index = {
                    a = 10
                }
            }
        }
    }    
  
    }
   
}
}


--]]

关键点在与容易混淆使用new方法,这种继承的方式实现的语义不够明确.
语法比较简单,没有super等关键字.难以实现方法的重写,protect,private,public语义,抽象类,接口等面向对象的高级特性.

总体而言,lua的语言比较适用于面向过程,而不太适合面向对象.

你可能感兴趣的:(Lua)