Lua基础语法

Lua基础语法

参考文档

  1. 《Lua程序设计(第二版)》 --电子工业出版社 [巴西]Roberto Ierusalimschy 著 周惟迪 译
  2. 菜鸟教程(RUNOOB.COM)

1. 类型与值

  • Lua是一种动态类型语言,在语言中没有类型定义的语法,任何变量都可以包含任何类型的值;Lua中有8中基础类型,可以使用函数type返回一个值的类型名称

  • nil

    print(type(nil))                --->nil
    
  • boolean布尔

    print(type(true))                --->boolean
    
  • number数字

    print(type(10))                  --->number
    
  • string字符串

    print(type("Hello world"))       --->string
    
  • userdata自定义类型

  • function函数

    print(type(print))        --->function
    
  • thread线程

  • table

    local a ={}
    print(type(a))            --->table
    
  • 如果一个变量没有初始化,它的类型为nil

    local a
    print(type(a))           --->nil
    

1.1 nil(空)

  • nil类型是一种特殊的类型,仅有一个值nil;其主要功能就是用于区分其他任何值,一个全局变量在第一次赋值前的默认值就是nil,将nil赋予一个全局变量等同于删除它,Luanil用于表述一种无效值的情况

1.2 Boolean(布尔)

  • boolean类型有两个值可以选择falsetrueLuafalsenil视为“假”,其余之外的其他值视为“真”

1.3 number(数字)

  • Lua中对于整数和浮点数不进行区分,全部使用number类型表示

1.4 string(字符串)

  • Lua中的字符串需要以一对匹配的单引号或者双引号来界定

  • 可以在字符串中使用类似于C语言中的转义字符

    • 使用字母进行转义,如\n 换行\r 回车

    • 使用ASCII码来指定字符串中的字符,如\97 a\10 \n

    • 如果不想使用转义序列,可以使用一对匹配的双方括号界定一个字符串Lua不会解释其中的转义字符,这种形式的字符串可以延伸多行

      a = "\97\97"
      b = [[\97\97]]
      
      print(a)          --->aa
      print(b)          --->\97\97
      
  • 字符串连接操作符..,当直接在一个数字后面输入它时必须要用一个空格来分隔它们,不然Lua会将第一个点理解为小数点,或者直接报错

    a = "abc"
    b = 123
    
    print(a..123)         --->abc123
    print(123 ..456)      --->123456
    
  • 在字符串前放置操作符#可以获得该字符串的长度

    a = "abc"
    
    print(#a)          --->3
    

1.5 table(表)

  • Lua 中的表table其实是一个"关联数组"associative arrays,数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过"构造表达式"来完成,最简单构造表达式是{},用来创建一个空表。

  • Lua table是不固定大小的,可以根据自己的需要进行扩充

  • Lua table 使用关联型数组,你可以用任意类型的值来作数组的索引,但这个值不能是 nil

  • Lua也是通过table来解决模块module、包package和对象Object的管理

1.5.1 table的特性
  • 一个table中包含许多成员,每个条目包含一个索引和一个元素,索引和元素为一组键值对
  • table的索引初始值默认为1,且索引不可以使用负数
  • table的长度可以动态增长
  • 不同的变量可以持有相同的table,一个持有table的变量与table自身之间没有固定的关联性
  • 当没有任何对table的引用时,Lua的垃圾回收器会删除该table并复用它的内存
1.5.2 table初始化与赋值
  • 在定义时初始化

    • 最简单的构造式就是一个空构造式即{},用于创建一个空table

      table_tese = {}
      
    • 也可以使用构造式在初始化table时进行赋值

      local local_A = "local_A"
      local num_3 = 3
      
      table_test1 = {
          --风格1.初始化时使用“=”赋值--
          style_a_1 = "a",
          style_a_2 = 1,
          style_a_3 = "2",
          style_a_4 = local_A,
      
          --风格2.使用将索引值防在方括号中--
          [1] = 1,
          [2] = "a",
          [num_3] = local_A,
      
          --风格3.Lua提供了一种更加通用的风格,这种格式允许使用负数或运算符作为索引--
          ["-1"] = "2",
          ["+"] = "3"
      }
      
      --风格4.仅填充元素值,索引值由1开始自动依次递增--
      table_test2 = { "a", "b", "c", "d" }
      
      --风格5.仅填充元素值,并指定初始索引值,索引值由初始值开始自动依次递增--
      table_test3 = { [0] = "a", "b", "c", "d" }
      
  • 在创建后修改table中的某些字段

    --风格1. 使用方括号--
    table_test["a"] = 3         --->将索引a对应的值更改为3
    table_test[1] = "c"         --->将索引1对应的值更改为c
    table_test[2] = "d"         --->新增索引2并将元素的值赋值为c
    
    --风格2.使用在table变量后使用"."连接索引值,但索引值不可以为数组--
    table_test.a = 3
    
    --删除元素,将索引对应的值置为nil--
    table_test.a = nil
    table_test[1] = nil 
    
1.5.3 table遍历
  • pairs可以遍历所有成员

    • Luajit中使用for key, value in pairs(table) do这样的遍历顺序并非是代码中table中的排列顺序,而是根据tablekeyhash值的排列顺序来遍历,keyhash是以时间戳相关的随机值作为种子生成的,导致最终打印的值的顺序是随机值
  • ipairs只遍历1,2,数值型下标,下一个3不连续就中断了

  • 由于#tbtest获取的值为4,故下标3的值为nil

    table_1 = {
        a = "a",
        [1] = "1",
        b = "b", 
        c = "c",
        [2] = "2",
        [4] = "4"
    }
    
    for key, value in pairs(table_1) do      
        print(tostring(key)..":"..tostring(value))
    end
    结果:
    1:1
    2:2
    4:4
    a:a
    b:b
    c:c
    
    for key, value in ipairs(table_1) do      
        print(tostring(key)..":"..tostring(value))
    end
    结果:
    1:1
    2:2
    
    for i=1, #table_1 do 
        print(tostring(i)..":"..tostring(table_1[i]))
    end
    结果:
    1:1
    2:2
    3:nil
    4:4
    
    local table_1 ={
        {name = "a", value = "1"},
        {name = "c", value = "3"},
        {name = "d", value = "4"},
        {name = "b", value = "2"},
    }
    
    i = 0
    for i, k in pairs(table_1) do    
        for key, value in pairs(k) do    
            print(tostring(key)..":"..tostring(value))
        end
        print("\n")
    end
    结果:
    name:a
    value:1
    
    name:c
    value:3
    
    name:d
    value:4
    
    name:b
    value:2
    

2. 语句

  • Lua支持的常规语句基本上和C语言中的差不多,这些语句包括赋值、控制结构、过程调用等。另外Lua还支持一些不太常见的语句,比如多重赋值和局部变量声明

2.1 多重赋值语句

  • Lua允许多重赋值,也就是一次将多个值赋给多个变量,每个值和变量之间用逗号分隔

    • 如果等号两侧的值数量不同的话,多余的变量会被赋值为nil,如果值的个数更多的话,多余的值会被丢弃

      x, y = 10, 10*10
      
      print(x.. ":" ..y)        --->10:100
      

2.2 局部变量和块

  • 相对于全局变量,Lua还提供了局部变量,通过local语句来创建局部变量

    • 局部变量的作用域仅限于声明它们的那个块

      一个块是一个控制结构的执行体、或者是一个函数的执行体或是一个程序块

  • 局部变量通常会随着其作用域的结束而消失,相对应的内存资源由垃圾回收机制进行回收

3.控制结构

  • 控制结构中的条件表达式可以是任何值,Lua将所有不是falsenil的值视为真

3.1 if then else

  • 判断条件放置在ifthen之间,函数体在then之后,整个判断语句程序块以end结束

  • 多层嵌套结构使用else if语句

    if a > b then
        print("a > b")
    elseif a == b then
        print("a = b")
    else
        print("a < b")
    end
    

3.2 while循环

  • 先判断while的条件是否为真,如果条件为假那么结束循环,不然执行循环体并重复这一过程

    local i = 1
    while a[i] do
        print(a[i])
        i = i + 1
    end
    

3.3 repeat循环

  • 可视为C语言中的do while语句

  • 一条repeat-until语句重复执行其循环体直到条件为真时结束。检测条件是在循环体之后做的,循环体会至少执行一次

    --打印输入的第一行不为空的内容--
    repeat
        line = io.read()
    until line ~= ""
    print(line)
    

3.4 for循环

  • for语句有两种形式:数字型for和泛型for
3.4.1 数字型for循环
  • varexp1变化到exp2每次变化都以exp3作为步长递增var,并执行一次循环体。第三个表达式exp3是可选参数,如果不指定的话,Lua会将步长默认为1

    for var = exp1, exp2, exp3 do
        <执行体>
    end
    
    --例如,依次打印从100到1--
    for i = 100, 1, -1 do
        print(i)
    end
    
  • 如果不想给循环设置上限的话,可以使用常量math.huge

    --无限循环打印--
    for i = 1, math.huge do
        print(i)
    end
    
  • for循环中可以使用break语句退出循环

  • for循环的三个表达式是在循环开始前一次性求值的

  • 控制变量会被自动的声明为for循环语句的局部变量,并且仅在循环体内可见,因此控制变量在循环结束后就不存在了

3.4.2 泛型for循环
  • 泛型for循环通过一个迭代器函数来遍历所有值

    for key, value in pairs(table_1) do      
        print(tostring(key)..":"..tostring(value))
    end
    
3.4.3 迭代器
  • 迭代器是一种可以遍历集合中所有元素的机制,lua中迭代器使用函数表示,每调用函数一次,返回集合中的下一个元素

    • 迭代器的使用方法是定义一个工厂,然后利用这个工厂生产闭包,然后调用这个闭包来获取集合中的元素。这种方式的缺点就是每次都得创建一个新的闭包,有一定的开销

    • 使用迭代器的方式也很简单,直接调用迭代器就相当于调用闭包函数,迭代器会返回元素集合的下一个元素,当返回nil时就代表元素遍历完了

      function value(t)
          local i = 0
          return function()
              i = i + 1
              return t[i]
          end
      end
      
      t = {1, 2, 3, 4, 5}
      -- 创建一个迭代器
      iter = value(t)
      -- 遍历迭代器
      while true do
          local ele = iter()
          if ele == nil then
              break
          end
          print(ele)
      end
      
  • 无状态迭代器

    • 无状态迭代器指的是在循环过程中,不保留任何状态的迭代器,这样我们就可以避免创建闭包所花费的代价。典型的无状态迭代器就是:ipairs

    • 泛型 for 循环的设计本身就包含迭代器,而且是一种无状态的迭代器,即不需要创建闭包

    • 泛型 for 循环内部保存了迭代器状态,包括迭代器函数、控制变量和恒定状态

      • 迭代器函数:迭代器工厂产生的匿名函数,不是闭包
      • 控制变量:for 循环使用这个变量控制当前遍历的位置及何时结束遍历
      • 恒定状态:迭代器遍历的目标,一般是指 table
      iter = function(t, i)
          i = i + 1
          local v = t[i]
          if v then
              return i, v
          end
      end
      
      ipairs = function(t)
          return iter, t, 0
      end
      
      for i, v in ipairs(t) do
          print(i, v)
      end
      
  • 多状态迭代器

    • 泛型 for 循环可以实现只有一个恒定状态的迭代器,如果有多个状态需要保存就不行了。如果要保存多个状态,可以有下面两种方式

      • 使用闭包
      • 把多个状态封装在一个 table 里面,然后使用泛型 for 循环
      local array = {"Google", "Runoob"}
      
      function elementIterator (collection)
         local index = 0
         local count = #collection
         -- 闭包函数
         return function ()
            index = index + 1
            if index <= count
            then
               --  返回迭代器的当前元素
               return collection[index]
            end
         end
      end
      
      for element in elementIterator(array)
      do
         print(element)
      end
      

4. 函数

  • 参数调用,需要将所有参数放到一对圆括号中,即使调用函数时没有参数也必须写出一对圆括号

    • 若一个函数若只有一个参数,并且此参数是一个字面字符串或table构造式,那么圆括号便是可有可无的

      print "Hello Morld"         --->Hello Morld
      print("Hello World")        --->Hello Morld
      
      print(type{})               --->table
      print(type({}))             --->table
      
    • Lua为面向对象式的调用也提供了一种特殊的语法——冒号操作符。表达式o.foo(o, x)的另一种写法是o:foo(x),冒号操作符使调用o.foo时将o隐含地作为函数的第一个参数。

  • 关于实参和形参,Lua会自动调整实参的数量,以匹配参数表的要求。这项调整与多重赋值很相似,即“若实参多余形参,则舍弃多余的实参;若实参不足,则多余的形参初始化为nil

4.1 多重返回值

  • Lua具有一项非常与众不同的特征,允许函数返回多个结果,只需在return关键字后列出所有的返回值即可
  • 如果一个函数没有返回值或者没有返回足够多的返回值,那么Lua会用nil来补充缺失的值
  • table构造式可以完整地接收一个函数调用的所有结果,即不会有任何数量方面的调整:

4.2 变长参数

  • Lua参数传递时可以将所有参数全部放在一个table中,然后将这个table作为唯一参数传递;在函数内部,对必要参数进行检查,这样也可以实现变长参数的效果

  • Lua中的函数可以接受不同数量的实参

    • 参数表中的3个点...表示该函数可接受不同数量的实参,一个函数要访问它的变长参数时,仍需用到3个点...但不同的是,此时这3个点是作为一个表达式来使用的

      function foo(...)
          local a,b,c = ...
      end
      
      function foo(...)
          return ...
      end
      
  • 具有变长参数的函数同样也可以拥有任意数量的固定参数,但固定参数必须放在变长参数之前。Lua会将前面的实参赋予固定参数,而将余下的实参(如果有的话)视为变长参数

    function foo(fmt, ...)
        return string.format(fmt, ...)
    end
    
    local a, b, c = foo("a")                 --->a,nil,nil
    local a, b, c = foo("%d, %d", 4, 5)      --->4,5,nil,nil
    
  • 通常一个函数在遍历其变长参数时只需使用表达式...,这就像访问一个table一样,访问所有的变长参数

    --select函数用于访问可变数量的参数列表中的参数
    for i = 1, select('#', ...) do      --遍历所有变长参数
        local arg = select(i, ...)      --得到第i个参数
        print(arg)
    end
    

5. 深入函数

  • Lua中,函数是一种“第一类值”,它们具有特定的词法域,“第一类值”是什么意思呢?这表示在Lua中函数与其他传统类型的值(例如数字和字符串)具有相同的权利。函数可以存储到变量中(无论全局变量还是局部变量)或table中,可以作为实参传递给其他函数,还可以作为其他函数的返回值。

    local network ={
        {name = "a", IP = "210.26.30.34"},
        {name = "c", IP = "210.26.30.23"},
        {name = "d", IP = "210.26.23.12"},
        {name = "b", IP = "210.26.23.20"},
    }
    
    --如果想以name字段、按反向的字符顺序来对这个table排序的话,只需这么写:
    table.sort(network, function (a, b) return(a.name > b.name) end)
    
  • Lua中函数可以作为一个变量被其他变量持有

5.1 闭合函数(闭包)

  • 若将一个函数写在另一个函数内,那么这个位于内部的函数便可以访问外部函数中的局部变量,这项特征称之为“词法域”

  • Lua中一个函数可以嵌套在另一个函数中,内部函数可以访问外部函数中的变量

    • 函数作为表达式时不能命名

      function foo_1(x)
          x = x + 1
          return function ()
              print(x)
              return x
          end
      end
      
      local foo_3 = foo_1(10)
      foo_3()          --->11
      
    • 外部函数中的变量生命周期与普通函数相同,在外部函数调用结束后变量释放

    • 内部函数中的变量生命周期与外部接收闭包的变量相同

    • 每次形成闭包后内部函数中的变量会单独申请一块内存

5.2 尾调用消除

  • 当一个函数调用是另一个函数的最后一个动作时,该调用算是一条尾调用

    function f (x) return g(x) end
    
  • 当外部函数调用内部函数后就没有其他事情可做,因此程序也不需要保存任何关于该函数的栈信息,及尾调用不会消耗栈空间

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值