Lua笔记 第一至第八章

Lua笔记 第一至第八章



转自: http://www.cnblogs.com/summericeyl/archive/2011/06/02/2068507.html


  • Lua特性: 可扩展性, 简单, 高效率, 与平台无关
    • 可通过新类型和函数来扩展其功能.

1.1 Chunks

  • Chunk是一系列语句
  • lua交互运行, CTRL-Z退出
    • lua -la -lb
      • 先运行a, 然后运行b(-l选项会调用require)
    • -i选项要求Lua运行指定Chunk后进入交互模式
    • dofile函数, 连接外部Chunk

1.2 全局变量

  • 赋值即创建, 未初始化的值为nil

1.3 词法约定

  • 保留字: and break do else elseif end false for function if in local nil not or repeat return then true until while
  • Lua 大小写敏感
  • 单行注释: --
  • 多行注释: --[ [ --]]

1.4 命令行方式

  • lua [options] [script [args]]
    • -e 直接将命令传入Lua
      • lua -e "print(math.sin(12))"
    • -l: 加载一个文件
    • -i: 进入交互模式
    • _PROMPT 设置一个标量作为交互模式的提示符
              lua -i -e "_PROMPT=' lua> '"
      
    • LUA_INIT环境变量: @filename 表示加载指定文件. 如无@, 则假定filename为Lua代码文件并且运行它
    • 脚本名索引为0, 参数从1开始增加.
          lua -e "sin=math.sin" script a b
          arg[-3] = "lua"
          arg[-2] = "-e"
          arg[-1] = "sin=math.sin"
          arg[0] = "script"
          arg[1] = "a"
          arg[2] = "b"
      

第2章 类型和值

  • 八个基本类型: nil, boolean, number, string, userdata, function, thread, table

2.1 Nil

  • 全局变量未被赋值之前

2.2 Booleans

  • Lua所有的值都可以作为条件, 除了false和nil为假, 其他都为真

2.3 Number

  • 没有整数

2.4 Strings

  • 可使用单引号或双引号表示字符串
  • 转义符序列: \b 后退 \f 换页 \n 换行 \r 回车 \t 制表 ...
    • \[ \] 左中/左右括号
    • 还可以使用 [ [...]] 表示字符串, 这样可以多行, 可以嵌套也不会解释转义序列
    • 运行时, Lua自动在 string 和 numbers 之间自动进行类型转换, 使用算术操作符, string自动转换成数字, 或者其他需要字符串的时候, 数字转换成字符串
    • string转换成数字函数 tonumber(), 相反则为 tostring

2.5 Functions

  • 函数可以作为函数的参数, 也可以作为函数的返回值.
  • Lua可以调用lua或者C实现的函数, 标准库包括 string库, table库, I/O库, OS库, 算术库, debug库

2.6 Userdata and Threads

  • userdata可以将C数据存放在Lua变量中, userdata除了赋值和相等比较外没有预定义的操作.
  • userdata用来描述应用程序或者使用C实现的库创建的新类型.

第3章 表达式

3.1 算术表达式

  • 二元: + - * / ^(加减乘除幂)
  • 一元: - 负值

3.2 关系运算符

  • < > <= >= == ~=
    • 回 false和true
  • nil只能和自己相等
  • 通过引用比较tables, userdata, functions, 即仅当两者表示同一个对象时相等
  • 比较字符串按字母顺序比较
    • 注: "2" < "15" 返回 false 字母顺序

3.3 逻辑运算发

  • and or not
  • 只有 false 和 nil为假, 其他为真, 0也为真
  • and 的优先级比 or高

3.4 连接运算符

  • .. ---- 字符串连接

3.5 优先级

  • ^
  • not -
  • * /
  • + -
  • ..
  • < > <= >= ~= ==
  • and
  • or
  • 除了^和..之外, 所有二元运算符都是左连接的

    3.6 表的结构

    • 最简单的构造函数是{}, 用来创建一个空表, 可以直接初始化数组
          days = {"Sunday", "Monday", "Tuesday", "Wednesday",
              "Thursday", "Friday", "Saturday"}
      
      • 第一个元素索引为1
        • 即"Sunday" = days[1]
    • 元素可以为表达式
    • 初始化一个表为record
          a = {x = 0, y = 0}            <-->    a={}; a.x=0; a.y=0
      
    • 不管何种方式创建table, 我们都可以向表中添加或者删除任何类型的域, 构造函数仅仅影响表的初始化
    • 给元素赋值nil可以移除该元素
    • 每次调用构造函数, Lua都会创建一个新的table, 可以使用table构造一个list
    • 在同一个构造函数中可以混合列表风格和record风格进行初始化
      • 用[expression]显示的表示将被初始化的索引
                opnames = {["+"] = "add", ["-"] = "sub",  ["*"] = "mul", ["/"] = "div"}
                {x = 0, y = 0}  <-->  {["x"] = 0, ["y"] = 0}
                {"red", "green", "blue"}  <-->  {[1]= "red", [2] = "green", [3] = "blue"}
        
      • 如果想从下标0开始
            days = { [0] = "Sunday", "Monday", "Tuesday", "Wednesday",
                "Thursday", "Friday", "Saturday"}
        
      • 构造函数中域分隔符可用"," 或者";"表示, 一般";"分割不同类型的表元素

    第4章 基本语法

    4.1 赋值语句

    • 可同时对多个变量同时赋值
          a, b = 10, 2*x   <-->   a = 10; b = 2*x
      
      • 先计算右边所有的值, 然后再进行赋值操作, 所以可以进行交换
            x, y = y, x
            a[i], a[j] = a[j], a[i]
        
      • 当变量个数和值的个数不一致
        • a. 变量个数 > 值的个数 按变量个数补足nil
        • b. 变量个数 < 值的个数 多余的值会被忽略
    • 函数返回值
      • a, b = f()
        • f()如果返回两个值, 第一个赋值给a, 第二个赋值给b

    4.2 局部变量和代码块(block)

    • local 创建一个局部变量, 与全局变量不同, 局部变量只在被声明的那个代码块内有效.
    • 代码块: 一个控制结构内, 一个函数体, 或者一个chunk(变量被声明的那个文件或者文本串)
    • 尽可能使用局部变量: 1. 避免命名冲突, 2. 访问局部变量比全局变量快

    4.3 控制结构语句

    • if语句, 有三种形式:
              if condition then
                  then-part
              end;
              
              if condition then
                  then-part
              else
                  else-part
              end;        
      
              if conditions then
                  then-part
              elseif conditions then
                  elseif-part
              ..              --->多个elseif
              else
                  else-part
              end;            
      
    • while 语句
              while condition do
                  statement;
              end;
      
    • repeat-until 语句
              repeat
                  statements;
              until conditions
      
    • for 语句
    • 两大类:
      • 第一, 数值 for 循环
                for var = exp1, exp2, exp3 do
                    loop-part
                end
        
        • exp1(初始值), exp2(终止值), exp3表示进行的计算, 默认exp3 step = 1
        • 控制变量var是局部变量自动被声明, 并且只在循环内有效
        • 退出循环用break语句
      • 第二, 范型for循环
                for i, v in ipairs(a) do print(v) end
        
        • 范型for遍历迭代子函数返回的每一个值.

    4.4 break和return语句

    • break 退出当前循环
    • return 函数返回结果.
    • break和return只能出现在block的结尾一句
    • 如果要在中间使用, 可以使用 do..end来实现

    第5章 函数

    • 语法:
          function func_name (arguments-list)
              statements-list;
          end;
      
    • 当函数只有一个参数并且这个参数是字符串或者表结构的时候, ()可有可无
          print "Hello world"
          dofile "a.lue"
      	-- 打印多行 --
          print [[a multi-line message]]
          f{x = 10, y = 20}
          type{}
      
    • 面向对象调用函数的语法, 如 o:foo(x)和o.foo(o,x) 是等价的

    5.1 多返回值

    • Lua 函数中, 在return后列出要返回的值列表即可返回多值, 如: return m, m1
    • 注意返回值的几个情况:
      • 赋值语句, 如果调用函数表达式作为最后一个参数或者仅有一个参数, 根据变量个数函数尽可能返回多个值, 不足补nil
      • 其他情况, 函数调用仅返回第一个值
      • 例如 foo2() 返回 'a', 'b'
        • x, y = foo2(), 20 -- x = 'a', y =20
      • 另外, 函数调用表达式作为函数参数被调用的时候, 和多值赋值是相同的
                print(foo2())        --> a b
                print(foo2(), 1)        --> a 1
                print(foo2().."x")        --> ax
        
      • 函数调用表达式在表构造函数中初始化时, 和多值赋值时相同
                a = {foo2()}        -- a = {'a', 'b'}
                a = {foo2(), 4}        -- a = {'a', 4}
        
      • return f()表示返回"f()的返回值", 使用圆括号强制返回一个值
                print(foo2())        --> a b
                print((foo2()))        --> a
        
      • 一个return语句如果使用圆括号将返回值括起来也将导致返回一个值
      • 函数多值返回的特殊函数unpack, 接受一个数组作为输入参数, 返回数组的所有元素
        • Lua中如果想调用可变参数的可变函数只需要: f(unpack(a))

    5.2 可变参数

    • Lua 和C语言类似在函数参数列表中使用三点(...)表示函数有可变的参数
    • Lua将函数的参数放在一个叫arg的表中, 除了参数之外, arg表中还有一个域n表示参数的个数
    • 需要几个固定参数加上可变参数
          function g(a, b, ...) end
              g(3)                a = 3, b = nil, arg = {n=0}
              g(3,4)                a = 3, b = 4, arg = {n=0}
              g(3,4, 5, 8)                a = 3, b = 4, arg = {n=2}
      
    • 哑元: 下划线
              local _, x = string.find(s, p)
      
    • 函数 string.format: 文本格式化, 类似C的sprintf函数

    5.3 命名函数

    第6章 再论函数

    • 函数可以没有名字, 匿名的.函数名实际上是一个指向函数的变量
              a = {p = print}
              a.p("Hello world")
              print = math.sin
              a.p(print(1))
              sin = a.p
              sin(10, 20)
      
    • Lua中
              function foo (x) return 2*x end
          	-- 等效于 --
              foo = function (x) return 2*x end
      
    • table 标准库提供了一个排序函数, 接受一个表作为输入参数并且排序表中的元素, 该函数传入表和排序函数, 例如:
              network = {
                  { name = "grauna", IP = "210.26.30,34" },
                  { name = "arraial", IP = "210.26.30.23" },
                  { name = "lua", IP = "210.26.23.12" },
                  { name = "derain", IP = "210.26.23.20" },
              }
          	-- 进行排序: --
              table.sort(network, function(a, b)
                  return (a.name > b.name)
              end)
      

    6.1 闭包

    • 当一个函数内部嵌套另一个函数定义时, 内部的函数体可以访问外部的函数的局部变量
              names = {"Peter", "Paul", "Mary"}
              grades = {Mary = 10, Paul = 7, Peter = 8}
              table.sort(names, function (n1, n2)
                  return grades[n1] > grades[n2]
                  end)
      
    • 创建函数实现该功能:
              function sortbygrade(names, grades)
                  table.sort(names, function(n1, n2)
                      return grades[n1] > grades[n2]
                  end)
              end
      
    • 在函数内部的匿名函数访问的变量为外部的局部变量或者upvalue

    6.2 非全局函数

    • 表和函数放在一起
              Lib = {}
              Lib.foo = function(x, y) return x+y end
              Lib.goo = function(x, y) return x-y end
      
    • 使用表构造函数
              Lib = {
                  Lib.foo = function(x, y) return x+y end
                  Lib.goo = function(x, y) return x-y end
              }
      
    • Lua 提供另一种语法方式
              Lib = {}
              function Lib.foo (x, y)
                  return x+y
              end
              function Lib.goo (x, y)
                  return x-y
              end        
      
    • 声明局部函数的两种方式
      • 方式一:
                    local f = function (...)
                        ...
                    end
                    
                    local g = function (...)
                        ...
                        f()
                        ...
                    end
        
      • 方式二
                    local function f(...)
                        ...
                    end
        
    • 使用非递归局部函数是要先定义而后使用

    6.3 正确的尾调用

    • 函数的最后一个动作是调用另外一个函数时, 我们称这种调用为尾调用
              function f(x)
                  return g(x)
              end
      
    • 尾调用不需要使用栈空间, 所以尾调用递归的层次可以无限制的
    • 可以将尾调用理解成一种goto
    • 见状态机, 房间四个门的例子, 适合于游戏中寻路

    第7章 迭代器与范型

    • 闭包是一个内部函数, 它可以访问一个或者多个外部函数的外部局部变量. 每次闭包的成功调用后这些外部局部变量都保存他们的值(状态)
      • 一个典型的闭包的结构包含两个函数: 一个是闭包自己, 另一个是工厂(创建闭包的函数)
      • 我们为list写一个简单的迭代器
                function list_iter (t)
                    local i = 0
                    local n = table.getn(t)
                    return function()
                        i = i + 1
                        if i <= n then return t[i] end
                    end
                end
        
      • list_iter是一个工厂, 每次调用都会调用一个新的闭包(迭代器本身). 闭包保存内部局部变量(t, i, n)
      • 当list中没有值时, 返回nil
                t = {10, 20, 30}
                iter = list_iter(t)
                while true do
                    local element = iter()
                    if element == nil then break end
                    print(element)
                end
        
      • 用于范性for语句
                t = {10, 20, 30}
                for element in list_iter(t) do
                    print(element)
                end
        
      • 这里为迭代循环处理所有的蒲记(bookkeeping): 首先调用迭代工厂; 内部保留迭代函数, 因为我们不需要iter变量; 然后在每一个新的迭代处调用迭代器函数, 当迭代器返回nil时循环结束

    7.2 范型 for 的语义

        for <var-list> in <exp-list> do
            <body>
        end
    
    • var-list 是以一个或多个逗号分隔的变量名列表. <exp-list> 是以一个或多个逗号分隔的表达式列表, 通常情况下exp-list只有一个值: 迭代工厂的调用
              for k,v in pairs(t) do
                  print(k, v)
              end
      
    • 也可以只有一个变量
              for line in io.lines() do
                  io.write(line, '\n')
              end
      
    • 我们称第一个变量为控制变量, 其值为nil时循环结束
    • 下面我们看看范型for的执行过程
      • 首先, 初始化, 计算in后面表达式的值, 表达式应该返回范性for需要的三个值: 迭代函数, 状态常量, 控制变量. 如返回的结果不足三个会自动用nil补足
      • 第二, 将状态常量和控制变量作为参数调用迭代函数(对for结构来说, 状态常量没有用处, 仅仅在初始化时获取他的值并传递哦给迭代函数)
      • 第三, 将迭代函数返回的值赋给变量列表
      • 第四, 如果返回的第一个值为nil循环结束, 否则执行循环体
      • 第五, 回到第二步再次调用迭代函数
    • 如我们的迭代函数是f, 状态常量是s, 控制变量的初始值为a0, 则循环: a1 = f(s, a0), a2 = f(s, a1), ... 知道 ai = nil

    7.3 无状态的迭代器

    • 无状态迭代器是指不保存任何状态的迭代器, 在循环中我们可以利用无状态迭代器避免创建闭包花费额外的代价
    • 每一次迭代, 迭代器都是用两个变量(状态变量和控制变量)的值作为参数被调用, 一个无状态的迭代器只利用这两个值可以获取下一个元素
              a = {"one", "two", "three"}
              for i, v in ipairs(a) do
                  print(i, v)
              end
      
    • ipairs的实现
              function iter (a, i)
                  i = i + 1
                  local v = a[i]
                  if v then
                      return i, v
                  end
              end
              
              function ipairs(a)
                  return iter, a, 0
              end
      

    7.4 多状态的迭代器

    • 迭代器保存多个状态信息, 最简单的方法是用闭包, 还有一种方法就是将所有的状态信息封装到table内, 将table作为迭代器的状态常量.
          local iterator
          function allwords()
              local state = {line = io.read(), pos = 1}
              return iterator, state
          end
          function iterator (state)
              while (state.line)
                  local s, e = string.find(state.line, "%w+", state.pos)
                  if a then
                      state.pos = e + 1
                      return string.sub(state.line, s, e)
                  else
                      state.line = io.read()
                      state.pos = 1
                  end
              end
              return nil
          end
      
    • 尽可能使用无状态迭代器, 不行则尽可能使用闭包, 尽量不要使用table这种方式

    7.5 真正的迭代器

        function allwords (f)
            for l in io.lines() do
                for w in string.gfind(l, "%w+") do
                    f(w)
                end
            end
        end
    
    • 打印单词 allwords(print)
    • 匿名函数
              local count = 0
              allwords(function (w)
                  if w == "hello" then count = count + 1 end
              end)
              print(count)
          for循环
              local count = 0
              for w in allwords() do
                  if w == "hello" then count = count + 1
              end
              print(count)
      

    第8章 编译.运行.错误信息

    • 完全简单的功能dofile比较方便, 读入文件编译并且执行.
    • loadfile读取文件编译但不执行, 如发生错误, 返回nil和错误信息, loadfile只需要编译一次, 但可多次运行. dofile每次都要编译
    • loadstring和loadfile类似, 只是它从一个串中读入
          f = loadstring("i = i + 1")
          i = 0
          f()    -->执行该代码  i=1
          f()       --> i=2
      
    • Lua把每一个chunk都作为一个匿名函数处理, chunks可以定义局部变量也可以返回值
          f = loadstring("local a = 10; return a + 20")
          print(f())
      
    • loadstring和loadfile都不会抛出错误, 如果发生错误他们将返回nil加上错误信息
    • 如果你想快捷的调用dostring, 可以这样: loadstring(s)()
    • 为了返回更清楚的错误信息可以使用assert: assert(loadstring(s))()
    • 使用loadstring, 可以只编译一次, 多次使用, 另外它不关心词法范围

    8.1 require 函数

    • require函数加载运行库. 与dofile完成相同的功能, 但有两点不同:
      • require 会搜索目录加载文件
      • require 会判断是否文件已经加载避免重复加载同一文件.
    • require的路径是一个模式列表, 例如: ?;?.lua;c:\windows\?;/user/local/lua/?/?.lua
      • 为了确定路径, Lua首先检查全局变量LUA_PATH是否为一个字符串, 而后判断这个字符串是否为路径. 如果失败则使用固定的路径 "?;?.lua"
      • Lua保留一张所有已加载的文件列表, 全局变量_LOADED访问文件名列表, 通过设置其值为nil, 可以让require重新加载相同的文件, 比如文件"foo"
                _LOADED["foo"] = nil
        
    • 路径模式可以不包含问号, 而只是一个固定的路劲: ?;?.lua;/usr/local/default.lua
    • _REQUIREDNAME保存被required的虚文件的名称
      • 如, 我们把路径设置为 "/usr/local/default.lua", 这样每次调用require都会运行newrequire.lua, 然后在使用_REQUIREDNAME的值实际加载required的文件

    8.2 C Package

    • 在Lua提示符下运行print(loadlib())看返回的结果, 如果显示bad arguments则说明你的发布版本支持动态连接机制, 否则说明动态连接机制不支持或者没有安装
    • Lua的loadlib函数内提供了所有动态连接的功能, 有两个参数: 库的绝对路径和初始化函数
              local path = "/usr/local/lua/lib/libluasocket.so"
              local f = loadlib(path, "luaopen_socket")
      
    • 加载指定的库, 并不打开库(即没有调用初始化函数), 返回初始化函数作为Lua的一个函数
              f()                --- 打开库
      

    8.3 错误

    • 你可以通过调用error函数显示地抛出错误, error的参数是要抛出的错误信息
              print "enter a number:"
              n = io.read("*number")
              if not n then error("invalid input") end
      
    • Lua提供了专门的内置函数来完成上面类似的功能
              print "enter a number:"
              n = assert(io.read("*number"), "invalid input")
      
    • assert 首先检查第一个参数, 若没问题, assert不作任何事情, 否则, assert以第二个参数作为错误信息抛出. 第二个参数是可选的
    • 对于程序逻辑上能够避免的异常, 以抛出错误的方式处理之, 否则返回错误代码

    8.4 异常和错误处理

    • 如果在lua中需要处理错误, 使用pcall函数封装你的代码
    • pcall在保护模式下执行函数内容, 同时捕获所有的异常和错误, 若一切正常, 返回true以及"被执行函数"的返回值, 否则返回nil和错误信息
    • 传递给error的任何信息都会被pcall返回

    8.5 错误信息和回跟踪(Tracebacks)

    • 不管什么情况下, Lua都尽可能清楚的描述问题发生的缘由
    • 函数error还可以有第二个参数, 表示错误发生的层级
    • cpcall实现功能, 两个参数: 调用函数, 错误处理函数
    • 最常用的debug处理函数: debug.debug和debug.traceback
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值