码迷,mamicode.com
首页 > 其他好文 > 详细

Lua4.0 参考手册(四)4.6-4.8

时间:2014-11-05 15:12:46      阅读:130      评论:0      收藏:0      [点我收藏+]

标签:io   ar   os   使用   for   sp   div   on   cti   

(接上篇)
-------------------
4.6 可见性和 Upvalue
-------------------
一个函数体可以引用它自己的局部变量(包括它的参数)和全局变量,只要它们没有被函数中同名的局部变量所隐藏(shadowed )。一个不可以使用包含它的函数的局部变量,因为这样的变量可能在函数调用的时候已经不存在了。然而,一个函数可通过 upvalue 使用包含它的函数中的局部变量。upvalue 的语法如下:
    upvalue ::= `%‘ name
一个 upvalue 多少有点像是一个变量表达式,但是它的值是冻结的(frozen)当使用它的函数实例化时。upvalue 中使用的名字可以是任何变量的名字,只要函数定义的时候该变量是可见的,也就是说,直接包含它的函数中的全局变量和局部变量。注意,当 upvalue 是一个表时,只有表的引用(也就是 upvalue 的值)是冻结的。表的内容是可以任意修改的。使用表的值作为 upvalue 可以让函数有可写的但是私有的状态。
下面是一些例子:

    a,b,c = 1,2,3 -- global variables
    local d
    function f (x)
      local b = {} -- x and b are local to f; b shadows the global b
      local g = function (a)
        local y -- a and y are local to g
        p = a -- OK, access local `a‘
        p = c -- OK, access global `c‘
        p = b -- ERROR: cannot access a variable in outer scope
        p = %b -- OK, access frozen value of `b‘ (local to `f‘)
        %b = 3 -- ERROR: cannot change an upvalue
        %b.x = 3 -- OK, change the table contents
        p = %c -- OK, access frozen value of global `c‘
        p = %y -- ERROR: `y‘ is not visible where `g‘ is defined
        p = %d -- ERROR: `d‘ is not visible where `g‘ is defined
      end -- g
    end -- f

-------------------
4.7 错误处理
-------------------
由于 Lua 是一个扩展语言,所有的 Lua 动作从宿主程序中的 C 代码调用 Lua 库中的一个函数开始。每当一个错误在 Lua 编译或执行时发生,函数 _ERRORMESSAGE 将被调用(如果它不是 nil 的话),然后相应的库中的函数 (lua_dofile, lua_dostring, lua_dobuffer  和 lua_call) 被终止,并返回一个错误状态。

内存分配错误是上面规则的一个例外。当内存分配失败,Lua 也许不能执行 _ERRORMESSAGE 函数。因此,对于这种错误,Lua 不调用 _ERRORMESSAGE 函数,而是,库中相应的函数立即带一个特别的错误码(ERRMEM)返回。这个和其它的错误码定义在 lua.h 中,参见 5.8 节。

_ERRORMESSAGE 唯一的参数是一个描述错误的字符串。这个函数的默认定义叫做 _ALERT,它打印信息到 stderr (参见 6.1 节)。标准 I/O 库重定义了 _ERRORMESSAGE 并且使用调试机制(参见 7 节)去打印一些额外的信息,比如调用堆栈回溯。

Lua 代码可能通过显式调用函数 error (参见 6.1 节)生成一个错误。Lua 代码可以使用函数 call (参见 6.1 节)捕获一个错误。

-------------------
4.8 标签方法
-------------------
Lua 提供一个强大的机制去扩展它的语义,叫做标签方法 (tag method)。一个标签方法是一个程序员定义的在 Lua 程序执行的特定关键点调用的函数,它允许程序员在这些关键点上改变标准的 Lua 行为。每一个这样的点叫做一个事件。

特定事件的标签方法根据事件中的所涉及值的标签被调用(参见 3 节)。函数 settagmethod 改变给定对(tag,event)关联的标签方法。它的第一个参数是标签,第二个参数是事件的名字(一个字符串,参见下面),第三个参数是新的方法(一个函数),或者 nil 用来恢复对(标签事件对)的默认行为。settagmethod 函数返回标签事件对之前的标签方法。一个于之对应的函数 gettagmethod 接收一个标签和一个事件名并返回于之关联的当前方法。

标签方法在下面的事件中被调用,由给定名字区分。标签方法的语义可以由 Lua 函数描述解释器在每个事件的行为来更好的解释。这个函数不仅展示什么时候会调用标签方法,也展示它的参数,返回值和默认的行为。这里展示的代码仅用于说明目的;解释器中真正的行为是硬编码的,并且它比这个模拟更加的高效。这些解释(rawget, tonumber, call, etc.)中使用的所有的函数在 6.1 节中描述。

``add‘‘:
当 + 运算被应用于非数值型的操作数时会调用到它。
下面的函数 getbinmethod 定义了 Lua 如何为一个二元运算选择一个标签方法。首先,Lua 尝试第一个操作数,如果它的标签没有为操作定义标签方法;那么 Lua 将尝试第二个操作数,如果它依然失败,那么将从标签 0 获得一个标签方法。

    function getbinmethod (op1, op2, event)
      return gettagmethod(tag(op1), event) or
             gettagmethod(tag(op2), event) or
             gettagmethod(0, event)
    end

使用这个函数, ``add‘‘ 事件的标签方法是:

    function add_event (op1, op2)
      local o1, o2 = tonumber(op1), tonumber(op2)
      if o1 and o2 then -- both operands are numeric
        return o1+o2 -- ‘+‘ here is the primitive ‘add‘
      else -- at least one of the operands is not numeric
        local tm = getbinmethod(op1, op2, "add")
        if tm then
          -- call the method with both operands and an extra
          -- argument with the event name
          return tm(op1, op2, "add")
        else -- no tag method available: default behavior
          error("unexpected type at arithmetic operation")
        end
      end
    end

``sub‘‘:
当 - 运算被应用于非数值型的操作数时会调用到它。 它的行为类似于 ``add‘‘ 事件。

``mul‘‘:
当 * 运算被应用于非数值型的操作数时会调用到它。 它的行为类似于 ``add‘‘ 事件。

``div‘‘:
当 / 运算被应用于非数值型的操作数时会调用到它。 它的行为类似于 ``add‘‘ 事件。

``pow‘‘:
当 ^ (幂)运算调用时,即使对于数值型操作数。

    function pow_event (op1, op2)
      local tm = getbinmethod(op1, op2, "pow")
      if tm then
        -- call the method with both operands and an extra
        -- argument with the event name
        return tm(op1, op2, "pow")
      else -- no tag method available: default behavior
        error("unexpected type at arithmetic operation")
      end
    end

``unm‘‘:
当一元运算 - 被应用于非数值型的操作数时会调用到它。

    function unm_event (op)
      local o = tonumber(op)
      if o then -- operand is numeric
        return -o -- ‘-‘ here is the primitive ‘unm‘
      else -- the operand is not numeric.
        -- Try to get a tag method from the operand;
        -- if it does not have one, try a "global" one (tag 0)
        local tm = gettagmethod(tag(op), "unm") or
                   gettagmethod(0, "unm")
        if tm then
          -- call the method with the operand, nil, and an extra
          -- argument with the event name
          return tm(op, nil, "unm")
        else -- no tag method available: default behavior
          error("unexpected type at arithmetic operation")
        end
      end
    end

``lt‘‘:
当比较运算被应用于非数值型或非字符串型的操作数时会调用到它。它相当于 < 操作符。

    function lt_event (op1, op2)
      if type(op1) == "number" and type(op2) == "number" then
        return op1 < op2 -- numeric comparison
      elseif type(op1) == "string" and type(op2) == "string" then
        return op1 < op2 -- lexicographic comparison
      else
        local tm = getbinmethod(op1, op2, "lt")
        if tm then
          return tm(op1, op2, "lt")
        else
          error("unexpected type at comparison");
        end
      end
    end

其它的比较运算符使用这个标签方法根据常见的等值性:
    a>b <=> b<a
    a<=b <=> not (b<a)
    a>=b <=> not (a<b)

``concat‘‘:
当连结运算被应用于非字符串型的操作数时会调用到它。

    function concat_event (op1, op2)
      if (type(op1) == "string" or type(op1) == "number") and
         (type(op2) == "string" or type(op2) == "number") then
        return op1..op2 -- primitive string concatenation
      else
        local tm = getbinmethod(op1, op2, "concat")
        if tm then
          return tm(op1, op2, "concat")
        else
          error("unexpected type for concatenation")
        end
      end
    end

``index‘‘:
当 Lua 试图返回一个索引不在表中的值时会调用到它。语义参见 ``gettable‘‘ 事件。

``getglobal‘‘:
当 Lua 需要一个全局变量的值时会调用到它。这个方法可以只为 nil 设置,且只为由 newtag 新建的标签设置。注意标签是全局变量的当前值。

    function getglobal (varname)
      -- access the table of globals
      local value = rawget(globals(), varname)
      local tm = gettagmethod(tag(value), "getglobal")
      if not tm then
        return value
      else
        return tm(varname, value)
      end
    end

函数 getglobal 在基本库中被定义(参见 6.1 节)。

``setglobal‘‘:
当 Lua 给一个全局变量赋值时会调用到它。对于数值,字符串,表和有默认标签的 userdata 不可以设置这个方法。

    function setglobal (varname, newvalue)
      local oldvalue = rawget(globals(), varname)
      local tm = gettagmethod(tag(oldvalue), "setglobal")
      if not tm then
        rawset(globals(), varname, newvalue)
      else
        tm(varname, oldvalue, newvalue)
      end
    end

函数 setglobal 在基本库中被定义(参见 6.1 节)。

``gettable‘‘:
当 Lua 调用一个索引变量时会调用到它。对于有默认标签的表不可以设置这个方法。

    function gettable_event (table, index)
      local tm = gettagmethod(tag(table), "gettable")
      if tm then
        return tm(table, index)
      elseif type(table) ~= "table" then
        error("indexed expression not a table");
      else
        local v = rawget(table, index)
        tm = gettagmethod(tag(table), "index")
        if v == nil and tm then
          return tm(table, index)
        else
          return v
        end
      end
    end

``settable‘‘:
当 Lua 设置一个索引变量时会调用到它。对于有默认标签的表不可以设置这个方法。

    function settable_event (table, index, value)
      local tm = gettagmethod(tag(table), "settable")
      if tm then
        tm(table, index, value)
      elseif type(table) ~= "table" then
        error("indexed expression not a table")
      else
        rawset(table, index, value)
      end
    end

``function‘‘:
当 Lua 试图调用一个不是函数的值时会调用到它。

    function function_event (func, ...)
      if type(func) == "function" then
        return call(func, arg)
      else
        local tm = gettagmethod(tag(func), "function")
        if tm then
          for i=arg.n,1,-1 do
            arg[i+1] = arg[i]
          end
          arg.n = arg.n+1
          arg[1] = func
          return call(tm, arg)
        else
          error("call expression not a function")
        end
      end
    end

``gc‘‘:
当 Lua 垃圾回收一个 userdata 时会调用到它。这个标签方法只可以在 C 中设置,并且不可以设置给有默认标签的 userdata。对于每个被垃圾回收的 userdata, Lua 作和下面函数等价的操作:

    function gc_event (obj)
      local tm = gettagmethod(tag(obj), "gc")
      if tm then
        tm(obj)
      end
    end

在一个垃圾回收周期中,userdata 的标签方法被以标签创建的逆序调用,也就是说,被调用的第一个标签方法是关联于程序中创建的最后一个标签。而且,在周期结束时,Lua 做等价于 gc_event(nil) 调用的事情。
(未完待续)

Lua4.0 参考手册(四)4.6-4.8

标签:io   ar   os   使用   for   sp   div   on   cti   

原文地址:http://my.oschina.net/xhan/blog/341044

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!