停用的 Lua Faq

lua-users home
wiki

[以下內容可能部分可用]

用 Lua 程式設計

如何宣告變數?

你不需宣告變數,Lua 沒有變數宣告,也不需要。

不過,為何你非要宣告變數呢?

部分人希望宣告變數來防止輸入錯誤。例如,以下程式碼

color="red"
print(colour)

將印出 nil,因為第 2 行錯誤輸入了 color,而並未定義 colour

為防止發生此類錯誤,同時找出未定義的變數,你可以使用 getglobal 標籤方法

function safe_getglobal(x)
  local v=rawgetglobal(x)
  if v then return v else error("undefined global variable "..x) end
end

settagmethod(tag(nil),"getglobal",safe_getglobal)

加入此程式碼後,任何嘗試使用未定義變數(值為 nil)的行為都將產生錯誤訊息。

如何檢查 Lua 中的型別?

Lua 是一種動態型別語言。這表示變數沒有型別,只有值才有。由於 Lua 提供存取值型別的管道(透過 type 函式),你可以撰寫自己的動態型別函式。以下是其中一種可能的實作方式。

若要檢查簡單型別,只需使用 type。檢查表格的案例較有趣,因為必須確認所有欄位都存在且填寫正確,才能符合預先定義的「範本」。因此,我們需要一種方式來描述表格的「型別」。如上所述,簡單型別由名稱描述,且名稱由 type 函式回傳。表格則由「型別範本」描述,這些範本是將每個欄位對應其所需型別的表格。以下是對使用者介面工具組好用的範例

TNumber="number"
TPoint={x=TNumber, y=TNumber}
TColor={red=TNumber, blue=TNumber, green=TNumber}
TRectangle={topleft=TPoint, botright=TPoint}
TWindow={title="string", bounds=TRectangle, color=TColor}

提供此類描述後,下列函式將檢查值是否具有給定的型別

function checkType(d, t)
  if type(t) == "string" then
    -- t is the name of a type
    return (type(d) == t)
  else
    -- t is a table, so d must also be a table
    if type(d) ~= "table" then
      return nil
    else
      -- d is also a table; check its fields
      local i,v = next(t,nil)
      while i do
        if not checkType(d[i],v) then
          return nil
        end
        i,v = next(t,i)
      end
    end
  end
  return 1
end

如何建立唯讀變數?

在大型專案中,以及在程式庫中,偶爾必須保護某些變數,使其無法重新定義。當載入多個程式庫時,而且你想要確認它們不會意外地重新定義彼此的函式時,這項功能特別好。您

你可以利用 setglobal 標籤方法保護 Lua 中的函式

function protect(x)
  error("cannot redefine "..x)
end

settagmethod(tag(protect),"setglobal",protect)

此程式碼應在載入所有程式庫後執行。

加入此程式碼後,任何嘗試重新定義函式的行為都將產生錯誤訊息。你仍可以重新定義函式,但你必須明確地這麼做

rawsetglobal("print",nil)
function print (x) ... end

若要將此架構擴充套用至其他種類的值,你需要使用表格包裝值,同時使用 setglobalgetglobal 標籤方法

RO=newtag()				-- tag for read-only values

function ROvalue(value)			-- make a read-only value
  local t={value=value}
  settag(t,RO)
  return t

end

function ROsetglobal(x)			-- protects assignment
  error(x.." is read-only")
end

function ROgetglobal(x,value)		-- get the actual value
  return value.value
end

settagmethod(RO,"getglobal",ROgetglobal)
settagmethod(RO,"setglobal",ROsetglobal)

tolerance=ROvalue(0.12)			-- make some read-only variables
color=ROvalue("red")
myprint=ROvalue(print)

此架構僅影響使用 ROvalue 建立的變數,因為只有這些變數擁有 RO 標籤。

要如何在 Lua 中撰寫區塊註解?

Lua 5.0 新增區塊註解語法

--Double-dashes begin a single-line comment

--[[Double-dashes immediately followed by double left square bracket
    Begin a multi-line comment
    That continues until the __matching__ double right square bracket,
    so you can do [[this]] too. ]]
以下有關區塊註解的資訊適用於 5.0 之前的版本。

Lua 沒有區塊註解的特殊語法。一種解決方案是將註解封裝在字串中

comment = [[
    this is my
    multi-line comment
]]

以下為此解決方案的變體,此變體方便暫停區塊程式碼

comment = [[
    disabled code
--]]

只需在指定項目的替碼中加上--即可啟用。

--comment = [[
    enabled code
--]]

如果區塊過大,您可能需要避免在 garbage collector 移除前,讓其浮動在記憶體中。接著,您可以撰寫

local comment = [[
    disabled code
--]] ; comment = nil

Luiz Henrique de Figueiredo 也建議

do local C=[[
--]] end

"C 隨即超出作用範圍。剖析器可以利用這一點 (但並未利用)。此外還有:"

local C= 1 or [[
<syntaxly correct code to comment out>
--]]

"程式碼產生器可以利用這一點 (但並未利用)。"

另一種解法是使用編輯器巨集,在所選行之前放置"-- ";或例如"--~ ",如此註解的區塊便會從周遭的正常註解中跳脫出來。

若要註解某個程式碼區塊,可以使用if nil then ... end。(請參閱下一個問題。)

要如何複製物件?

在 Lua 中,會透過參照處理表格。因此,如果x的值是表格,那麼指定項目y=x並不會複製表格:xy包含同一個表格。(換句話說,xy的值是同一表格的參照。)

如果您真正想要複製一個表格,可以使用內建函式next,如下面的程式碼所示

function clone(t)            -- return a copy of the table t
  local new = {}             -- create a new table
  local i, v = next(t, nil)  -- i is an index of t, v = t[i]
  while i do
    new[i] = v
    i, v = next(t, i)        -- get next index
  end
  return new
end

如果您想要深度複製,在new[i] = v之前,請加上if type(v)=="table" then v=clone(v) end

要如何使用表格建構集合?

在 Lua 中實作集合最好的方式是將其元素儲存在表格的鍵值中。當表格中對應的值不為nil時,表示元素存在於集合中。

下面是一些集合的程式碼片段

s={}			-- create an empty set
s[x]=1			-- insert element x into set s
s[x]=nil		-- remove element x from set s
x_is_present=s[x]	-- does s contain x?

多重集合類似於集合,除了元素可能出現多次。多重集合可以實作的方式類似於集合,不過是使用與元素相關聯的值作為其計數器。下面是一個多重集合的程式碼片段

-- insert element x into bag s
if s[x] then s[x] = s[x]+1 else s[x] = 1 end

-- remove element x from set s
if s[x] then
  s[x] = s[x]-1
  if s[x] == 0 then s[x] = nil end
end

要如何將數字轉換為字元?

使用 Lua 5.0,請使用string.char(n),甚至可以使用string.format("%c", n)。在 Lua 5.0 之前,請使用strchar(n)format("%c",n)

要如何讀取字詞、數字、帶引號的字串等?

[需要更新!]

read的樣式比對功能強大,但並非所有人都會撰寫樣式。以下是一些有用的樣式

Word (sequence of letters and digits)
        {{"%w%w*"}}
Word (sequence of non-white space characters)
        {{"%S%S*"}}
Integer
        {{"[+-]?%d%d*"}}
Real
        {{"[+-]?%d%d*[.]%d%d*"}}
Double quoted string
        {{'"[^"]*"'}}
Single quoted string
        {{"'[^']*'"}}

如果您要在讀取項目之前跳過空白,請在樣式之前放置"{%s*}"。例如,"{%s*}%S%S*"會跳過空白,然後讀取下一個字詞。

第 3.2 版讓這一切變得更簡單:您可以使用"*w"來讀取一個字詞,以及"*n"來讀取一個數字。請參閱手冊,以獲得預先定義樣式的完整清單。

要如何使用 Lua 撰寫自我複製程式?

使用任何語言撰寫自我複製程式很有趣,但並不總是容易,因為您必須小心引號。由於 Lua 提供備用引號,所以執行起來很簡單

y = [[ print("y = [[" .. y .. "]]\ndostring(y)") ]]
dostring(y)

版本注意事項:以上是 Lua 4.0。

若要取得其他語言的自我複製程式,請參閱 Quine 網頁 [1]。也可以閱讀關於自我複製程式運作方式的相關說明 [2]

如何儲存/復原全域環境?

[需要更新!]

執行下列程式碼

function save()
  local g={}
  foreachvar(function (n,v) %g[n]=v end)
  return g
end

function restore(g)
  foreach(g,setglobal)
end

自 3.1 版本開始,使用 C,搭配 lua_openlua_closelua_setstate,就能執行這些相同的操作,甚至更多。

讓 C 與 Lua 互通

如何編寫一個可從 Lua 呼叫的 C 函式?

[需要更新!]

您必須遵循一個簡單的協定:使用 lua_getparam 取得 Lua 的任何參數,確定參數類型是否正確並具備適當的 lua_is... 函式,搭配適當的 lua_get... 函式轉換參數,對轉換後的參數進行相關處理,並透過適當的 lua_push... 函式將結果推回 Lua。

來看一個實際範例,並將 getenv 函式匯出至 Lua。更多範例請參閱實作標準函式庫的程式碼。

C 中的 getenv 函式會取得一個字串,並回傳另一個字串。其原型是

char* getenv(char*);

因此,適當的呼叫函式為 lua_isstringlua_getstringlua_pushstring

void wrap_getenv(void) {
 lua_Object o=lua_getparam(1);
 if (lua_isstring(o))
  lua_pushstring(getenv(lua_getstring(o)));
 else
  lua_error("string expected in argument #1 to getenv");
}

自 3.0 版本開始,Lua 納入了簡化封裝寫作的輔助函式。有了這些函式,我們可以將 wrap_getenv 寫成

void wrap_getenv(void) {
 lua_pushstring(getenv(luaL_check_string(1)));
}

完成封裝後,您必須呼叫 lua_register("getenv",wrap_getenv) 以讓 Lua 能使用封裝。這是標準函式庫所執行的內容

如何將我最愛的 C 函式庫與 Lua 共同使用?

請為函式庫中的每個函式撰寫一份封裝,如上所述。

已提供數個可自動化此程序的解決方案。請參閱 LuaAddons 中的「Code wrappers」

什麼是「堆疊大小溢位」?

VersionNotice:這是 Pre-Lua5。

除了無限遞迴等簡單情況外,如果您在迴圈中從 C 呼叫 Lua,就會收到這則訊息,因為參數和回傳值會累積在 Lua 的堆疊中。例如,這段程式碼將會產生「堆疊大小溢位」

 for (i=0;;i++) {
  lua_pushnumber(i);
  lua_call("print");
 }

解決方案是將與 Lua 對應的程式碼用於呼叫 lua_beginblocklua_endblock

 for (i=0;;i++) {
  lua_beginblock();
  lua_pushnumber(i);
  lua_call("print");
  lua_endblock();
 }

引用自參考手冊:強烈建議使用明確的嵌套區塊。


RecentChanges · preferences
edit · history
最後編輯時間為 2009 年 6 月 3 日下午 1:28 GMT (diff)