Lua4.0 參考手冊(四)4.6-4.8

(接上篇)
-------------------
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 得到一個標籤方法。
express

    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'':
當一元運算 - 被應用於非數值型的操做數時會調用到它。
lua

    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'':
當連結運算被應用於非字符串型的操做數時會調用到它。
code

    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) 調用的事情。(未完待續)

相關文章
相關標籤/搜索