Lua - 语言学习笔记

来源:互联网 发布:js弹出窗口 编辑:程序博客网 时间:2024/05/29 12:32

看云风的Skynet的时候, 对Lua生出了很大的兴趣,抽出时间看了下Lua在线手册,特作此记录。
本文仅为Lua语言作为独立的脚本语言的部分知识速记。 虽然Lua被设计来作为宿主语言的嵌入脚本,不过简单的逻辑纯Lua也可以实现。

关键字和操作符

关键字

and       break     do        else      elseifend       false     for       function  ifin        local     nil       not       orrepeat    return    then      true      until     while

这些关键字包含了 :

  • 类型(值)关键字 nil true false function
  • 控制流关键字 do while , repeat until , if then else elseif,for in , 和 end break return
  • 逻辑运算关键字and or not
  • 作用域关键字 local
  • 以下常规关键字没有提供 :
    • continue
    • main

操作符

     +     -     *     /     %     ^     #     ==    ~=    <=    >=    <     >     =     (     )     {     }     [     ]     ;     :     ,     .     ..    ...
  • 不等于是 ~= 而不是 !=
  • 没有逻辑运算符 && ||  !  , 使用对应关键字 。
  • .. 表示字符串连接。
  • # 运算符表示计算大小(长度)!
table = {'a' ,  'b' ,[4]= '4', aa='bb'}str = "12345"print(#table)print(#str)

输出

-- 从索引1开始顺序计数,直到数字索引中断。并不代表table的真正大小,仅代表```ipairs``` 接口的返回pair数。2-- 计数字符串中字符个数 \0 不计数。5

类型

  • nil
    • 即是类型有事值 。
    • 在判断中表示false
  • boolean
    • nil 、 false 表示false
    • 其余全部表示true 包括 0 和字符 '\0'
  • number
    • 默认双精度
    • 可以通过 luaconf.h 文件修改 (重新编译Lua解释器)。
  • string
    • 没有字符的概念,全部是字符串。
    • 可以使用 ' '" " 或者 [=*[ ]=*]
    • \0 结尾 。
  • function
    • function也是变量类型
    • 支持闭包。
  • userdata
    • 表示 C (宿主) 数据 , 仅宿主代码可以创建或者修改 。
    • 利用元表(metatable)为它指定操作函数。
  • thread
    • 协程类型,基于单线程,需要手动切入切出。
  • table
    • 默认索引是数字,从1开始 。 ( 数字 0 索引是可用的, 只是默认索引不会去不使用 ipairs 函数也不会去检测 数字 0
    • 索引可以是除去nil 之外的所有类型 。
    • 一个table不必使用统一类型的索引。
    • 值可以是除去nil 之外的所有类型, a[i] = nil 意味着删除a[i]
    • 语法糖
      • a[b] 等价于 a.b
      • a={["test"]=1} 等价于 a= {test=1}

Table 的测试代码

-- Construct a tabletable_a = {'test';["x"] = 111 ; 'Woo',y='xx', [0] = 'test 0 number'}table_a[3]='2 set'table_a[5]='5 set'table_a['\0']='test 0 again'table_a['0']='test 0 character'-- Tarvel through a table's first part integer pairsfor i  , v in ipairs(table_a) do     print(i,v);end-- Tarvel through a table's all pairsfor i  , v in pairs(table_a) do     print(i,v);end

输出 :

-- ipair 循环输出 :1       test2       Woo3       2 set-- pair 循环输出 : 1       test2       Woo3       2 set5       5 sety       xxx       1110       test 0 again character -- 字符  0         test 0 again  -- \0 字符不显示0       test 0 number -- 数字 0 

(CSDN-BUG)

语句

分号

lua的语句总是可以使用分号结尾, 但是在没有歧义的情况下也可以不使用。 条件判断的括号也是可有可无 。

判断

if condition then elseif ( condition ) thenelseend 

循环

while

while condition do...end

repeat until

-- 其实这个就是C里面的 do ... while 循环repeat ...until condition

for

-- 给var一个初始值, 一个终止值 和一个步长, 循环之。-- 步长可以省略 , 省略则为1。for var = 1 , 10 , 2 do ...end

for in

-- Lua的 foreach 循环a_table = { 1, 12, 3}for i , v in pairs(a_table)do ...end 

(CSDN-BUG)

函数

函数定义 

普通函数

-- 直接定义function foo ( param1 ) -- like print(param1)...end-- 赋值定义给某个变量foo = function ( param1 ) ...end

添加到某个table / userdata 的metatable中

-- 定义给特定变量-- 方法1 : 仅仅绑定方法test={}test.op = function(param1)...end-- 然后如下使用 test.op('param')-- 方法2 :绑定且默认传入 “:” 前的变量为第一个参数,参数名字是self 。test={test1=1 , test2=2}test:op = function(param1) -- 使用 : 而不是 .    -- 已经默认传入test 作为self参数 , 直接如下使用print(self.test1 , self.test2)...end-- 然后如下使用这个函数。test:op('param') -- 使用 : 而不是 .

函数闭包

function newCounter()    local i = 0    return function()     -- anonymous function who hold i variable       i = i + 1        return i    endendc1 = newCounter()print(c1())  --> 1 print(c1())  --> 2

支持多返回值

参见

http://book.luaer.cn/_37.htm

(CSDN-BUG)

metatable

定义

Lua 中的每个值都可以用一个 metatable。 这个 metatable 就是一个原始的 Lua table , 它用来定义原始值在特定操作下的行为。 你可以通过在 metatable 中的特定域设一些值来改变拥有这个 metatable 的值 的指定操作之行为。 举例来说,当一个非数字的值作加法操作的时候, Lua 会检查它的 metatable 中 “__add” 域中的是否有一个函数。 如果有这么一个函数的话,Lua 调用这个函数来执行一次加法。
我们叫 metatable 中的键名为 事件 (event) ,把其中的值叫作 元方法 (metamethod)。 在上个例子中,事件是 “add” 而元方法就是那个执行加法操作的函数。
你可以通过 getmetatable 函数来查询到任何一个值的 metatable。
你可以通过 setmetatable 函数来替换掉 table 的 metatable 。 你不能从 Lua 中改变其它任何类型的值的 metatable (使用 debug 库例外); 要这样做的话必须使用 C API 。
每个 table 和 userdata 拥有独立的 metatable (当然多个 table 和 userdata 可以共享一个相同的表作它们的 metatable); 其它所有类型的值,每种类型都分别共享唯一的一个 metatable。 因此,所有的数字一起只有一个 metatable ,所有的字符串也是,等等。

__add(a, b)                     --对应表达式 a + b__sub(a, b)                     --对应表达式 a - b__mul(a, b)                     --对应表达式 a * b__div(a, b)                     --对应表达式 a / b__mod(a, b)                     --对应表达式 a % b__pow(a, b)                     --对应表达式 a ^ b__unm(a)                        --对应表达式 -a__concat(a, b)                  --对应表达式 a .. b__len(a)                        --对应表达式 #a__eq(a, b)                      --对应表达式 a == b__lt(a, b)                      --对应表达式 a < b__le(a, b)                      --对应表达式 a <= b__index(a, b)                   --对应表达式 a.b__newindex(a, b, c)             --对应表达式 a.b = c__call(a, ...)                  --对应表达式 a(...)

实例

重载 + 运算符

-- Create 2 table with same structfraction_a = {numerator=2, denominator=3}fraction_b = {numerator=4, denominator=7}-- Create a metatable fraction_op={}function fraction_op.__add(f1, f2)    ret = {}    ret.numerator = f1.numerator * f2.denominator + f2.numerator * f1.denominator    ret.denominator = f1.denominator * f2.denominator    return ret;end-- set metatable to tablesetmetatable(fraction_a, fraction_op)setmetatable(fraction_b, fraction_op)-- call __add operator fraction_s = fraction_a + fraction_b;print(fraction_s.numerator,fraction_s.denominator)

利用metatable实现面向对象编程

-- 实现简单的继承Father={ name = '1 Father'};function Father:Print()    print('I am '..self.name);endSon={};setmetatable(Son,{__index=Father});-- Print I am 1 Father if Son.Print was not defined.Son:Print();--实现new接口Person={}function Person:new(p)    local obj = p    if (obj == nil) then        obj = {name="ChenHao", age=37, handsome=true}    end    self.__index = self    return setmetatable(obj, self)endfunction Person:toString()    return self.name .." : ".. self.age .." : ".. (self.handsome and "handsome" or "ugly")endme = Person:new()print(me:toString())kf = Person:new{name="King's fucking", age=70, handsome=false}print(kf:toString())

(CSDN-BUG)

协程

接口

  • 创建
    • coroutine.create 返回thread
    • coroutine.swap 返回function , 调用之久resume
  • coroutine.resume 切入
  • coroutine.yield 切出
  • 查询
    • coroutine.status
    • coroutine.running

实例分析

function foo (a)    print("foo", a);    return coroutine.yield(2*a);endco = coroutine.create(function (a,b)        print("co-body", a, b);        local r = foo(a+1);        print("co-body-mid", r);        local r, s = coroutine.yield(a+b, a-b)        print("co-body-end", r, s);        return b, "end";    end);print("main 1", coroutine.resume(co, 1, 10))print("main 2", coroutine.resume(co, "e"))print("main 3", coroutine.resume(co, "x", "y"))print("main 4", coroutine.resume(co, "x", "y"))

输出 :

-- main coroutine start -- enter co coroutineco-body 1       10-- call foofoo     2-- yield to main coroutinemain 1  true    4-- resume to co coroutineco-body-mid     e-- yield to main coroutine  main 2  true    11      -9-- resume to co coroutine co-body-end     x       y-- yield to main coroutine main 3  true    10      end-- resume to co coroutine  failed main 4  false   cannot resume dead coroutine

(CSDN-BUG)

模块

加载模块

require (不重复的)加载模块文件

下理解是错误的( 虽然这种解释有利于理解文件 + table的常规模块文件) :

fp=require(“my”)-- 等价于 :fp= (function()--my.lua文件内容--end)

证明 :
一个仅仅定义了一个全局变量的模块mud.lua

a = 'test mudle string '

如果require相当于以文件内容定义了一个匿名函数,那么这个文件内部的所有变量定义可见权限都应该限于这个文件所定义的这个函数中。然而 :
调用mud 主文件main.lua

requrie ("mud")-- mud.lua 定义的a 是个全局可见的a。print(a)

dofile 加载并执行文件

loadfile 加载不执行文件

规范的模块

table主导,不污染全局环境的模块

  • 利用module函数简写
-- in module xxx.lua-- user can change file name as he need local ModuleName = ...  -- module suit require parametermodule(ModuleName ,package.seeall) -- all interfaces/variables are in file are in namespace ...function TestPrint()     Print(ModuleName)end-- while use the modulelocal xxx=require("xxx")xxx.TestPrint()
  • 全部手打
local M = {}; local modelName = ...;_G[modelName] = M; -- 保证外部使用require的参数可以引用模块内接口function M.quit()    print("quit");endfunction M.play()    print("play");    M.quit() -- 必须写明属于 Mendreturn M;  -- 返回模块table ,不必须,但是支持引用者本地用自己名字保存
1 0
原创粉丝点击