黑铁时代
Programing is not only one kind of technology, but also one kind of art.

1. table:

  a = {}; a[x]不等价于a["x"],a["x"]等价于a.x,a[0]不等价于a["0"];

  #a取出的是table中连续数组的长度,从下标1开始;如果a[1] = 2; a[10000] = 3; #a是1;如果a[2] = 1; #a是0;

  a = {[1]="a", [2]="b", [3]="c", "d", "e"}; 遍历结果是:d e c。因为数组下标默认从1开始,并且d作为1下标的值,e作为2下标的值;

  a = {[1]="a", [2]="b", [3]="c",}和a = {[1]="a", [2]="b", [3]="c"; "one", "two", "three"}都是合法的;

2. 类型转换:

  lua会自动进行类型转换:"10" + 1结果是11;"1.1"*"2"结果是2.2;"str" + 1运行出错;建议使用强制转换函数tonumber,tostring等;

3. 算数操作符:

  ==:表示类型和值都相等为true;两个table只有在引用同一个table时才会相等;在比较字符串和数字大小的时候,lua会引发错误,例如"2" < 12;

  (a and b) or c 相当于 a ? b : c;

  x = x or v 相等于 if (x) x = x; else x = v;

4. 赋值:

  a, b, c = 1, 2; 结果a=1,b=2,c=nil;

  a, b = 1, 2, 3; 结果a=1,b=2;

  a, b, c = 0; 结果是a=0,b=nil,c=nil,特别注意;

  x, y = y, x; 交换值,因为lua是先计算右边的表达式再进行复制;

  a, b = fun(); 函数返回多个结果;

5. local:

  local语句声明一个局部变量,lua的局部作用域主要有函数体和程序块(do-end then-else then-elseif then-end);

  尽可能使用局部变量的原因:

    (1)避免全局变量污染;

    (2)局部变量的访问速度更快,可以优化性能;

    (3)局部变量会在作用域结束的时候被垃圾回收器回收,减少不必要的内存浪费;

  local foo = foo;暂存全局变量中的值,防止foo被修改,还可以加速对foo的访问;

6. for:

  for i = 1, 5, 1 then do; i是局部变量,for循环完成之后会被销毁;

  for i, v in ipairs(a) do;迭代数组元素;

  for k, v in pairs(a) do;迭代所有元素;

7. 函数:

  lua函数可以返回多个值的情况:

    (1)x, y = foo(); x, y, z = 10, foo(); 如果函数调用是最后一个表达式,函数会返回尽可能多的返回值;

    (2)x, y = foo(), 2; 如果函数调用是第一个表达式,函数只返回一个返回值;

    (3)print(foo().."x"); 如果函数调用出现在表达式中,函数只返回一个返回值;

    (4)f(g()); g将返回值个数调整和f的参数个数一致;

    (5)return f(); 将返回f的所有返回值;

    (6)return (f()); ()表达式将强制只返回一个返回值;

    (7)t = {f()}; 可以使用数组包含所有返回值;

8. 变成参数:

  访问变成参数...的两种方式:

    (1)使用{...},想数组一样遍历其中的元素;

    (2)select("#", ...)获取长度,select(i, ...)获取i下表的元素;

9. 闭包的使用场景:

  单列模式,隐蔽数据;

  例如:

local _a = 1;
local _b = 2;
local _c = 3;

local _instance = {};
_instance.setvalue = function (a, b, c)
    _a = a;
    _b = b;
    _c = c;
end
_instance.print = function()
    print("instance value:", _a, _b, _c);
end

local module1 = {};
module1.getInstance = function()
    return _instance;
end

return module1;

  回调函数;

  例如:

function digitButton(digit)
    return Button{
        label = tostring(digit);
        action = function()
            print(digit)
        end
    }
end

  重新定义某些函数,并可以通过闭包保持对旧函数的访问;  

  例如:

do
    local oldsin = math.sin
    math.sin = function(x)
        return oldsin(x * math.pi / 180)
    end
end

10. 函数定义:

local fact = function(n)
    if n == 0 then return 1
    else return n*fact(n-1)
    end
end

  上诉递归函数的定义会出现错误,解释器在解释fact(n-1)的时候,会找不到定义,因为fact还没有完成定义。需要修改成:

  local fact

  fact = function(n)

  ...

  end

  或者:

  local function fact(n)

  ...

  end

11. 尾调用:

  只有在函数调用的最有一个语句是:return <func>(<args>); 才算是尾调用。

  以下情况都不算尾调用:

    function f() g() end; 因为还要处理g()的临时返回值;

    function f() return g() + 1; 还要做一次加法计算;

    function f() return (g()); 还要调整返回值;

  使用尾调用的好处是:没有函数调用栈的开销;

12. 编译:

  loadfile:编译一个lua文件,返回一个可执行的函数,调用函数就等于执行编译好的lua代码。

  dofile:编译lua文件,并且会执行,而去会处理错误,代码相当于:

    function dofile(filename)

      local f = assert(loadfile(filename))

      return f()

    end

  loadfile会返回错误,但是不会处理错误,所有需要用assert;

  dofile会在每次调用都重新编译,因此,如果需要一次编译,多次运行,则使用loadfile;

  例子1(编译时的运行环境):

  module1.lua

    gi = gi + 1;

    print(gi);

 

  test1.lua

    local gi = 4;

    local f = assert(loadfile(module1.lua));

    f(); // 错误, 因为在编译的时候,没有找到全局变量gi的定义,如果将test1中的gi的local去掉,则可以正常运行。

      // 说明loadfile的编译是在当前运行环境中进行编译,而不是在独立的运行环境中进行编译

    print(gi); // 4

  test2.lua

    gi = 4;

    local f = assert(loadfile(module1.lua));

    f(); // 5 调用f就像调用普通lua函数一样,所以gi会被递增

    f(); // 6

  test3.lua

    gi = 4;

    dofile(module1.lua); // 5 每次都会重新编译,所以两次调用结果一样

    dofile(module1.lua); // 5

  例子2:

  module1.lua

    function foo()

      print("foo");

    end

  test1.lua

    local f = assert(loadfile(module1.lua));

    foo(); // 错误,lua中函数的定义其实是一种赋值,是在运行时确定的,因此编译时无法找到foo的定义;

    f();

    foo(); // 打印foo,在运行f()后,定义就完成了;

  上面例子中,foo是全局的函数变量,如果改为局部变量local foo = funtion() print("foo") end,在test1.lua中将访问不了foo,即时是在f()之后;

13. 错误:

  assert(bool, message): lua中的断言可以中断程序的执行并打印错误堆栈;

  error:抛出一个lua错误,可以传入任意类型,包括字符串,数字或者table来标示错误信息;

  pcall:pcall称为保护调用,通常用来捕获异常和处理异常。pcall在调用一个函数的时候,如果函数正常运行,返回true和函数返回值;如果出现错误,返回false和错误消息;

  xpcall:同样也是保护调用,和pcall不同的是,xpcall的第二个参数可以提供一个异常处理函数,让用户可以自己处理异常情况。正常运行时候和pcall的机制一样,当出现异常后,返回fasle和nil,并且会调用异常处理函数;

  例子:

  function foo()

    if (s == 1) then return 5;

    else error("foo error"); end

  end

  s = 1;

  local status, data = pcall(foo); // status = true, data = 5

  s = 2;

  local status, error = pcall(foo); // status = false, error = "foo error"

  assert和error都可以产生异常,但是error可以自定义错误的信息,更加灵活;

  例子:

  local status, data = xpcall(foo, function()

    print(debug.traceback()); // 打印异常发生时的调用栈信息 

  end);

posted on 2016-07-05 16:16  黑铁时代  阅读(240)  评论(0编辑  收藏  举报