串接字串
s1="a"
s2="b"
s3 = s1 .. s2
print("s3:",s3 )  --印出 s3  ab

=======================================================

計算長度

print(#'abcd')
4
print(#'Hello, world!')
13
aa = 'Hello, world!'
print(#aa)
13
print(string.len(aa))
13
=======================================================

 

metatable

 在一些博客上看到這個詞被譯作元表, 我更偏向把它稱作重載表, 因為metatable的作用更像是重載(override)對應表的操作行為的(比如+, *).

構成metatable的方式是一個metatable掛接一個table, 如下所示:
tbl1 = {"alpha", "beta", "gamma"}
mt = {}
setmetatable(tbl1, mt)
可以用getmetatable()語句來檢視一個table所掛接的metatable.
> print(getmetatable(tbl1) == mt)
true
metatable通過其包含的函數來給所掛接的table定義一些特殊的操作,包括:
__add: 定義所掛接table的加法操作
__mul: 定義乘法操作
__div: 定義除法操作
__sub: 定義減法操作
__unm: 定義負操作, 即: -table的含義
__tostring: 定義當table作為tostring()函式之參數被呼叫時的行為(例如: print(table)時將呼叫tostring(table)作為輸出結果)
__concat: 定義連接操作(".."運算符)
__index: 定義當table中不存在的key值被試圖獲取時的行為
__newindex: 定義在table中產生新key值時的行為
 
__add, __mul, __div, __sub, __unm
例如, 可以用下方的語句定義table的加法行為並使用它.
function mt.__add(a,b)
 local result = setmetatable({}, mt)
 
 for i = 1, #a do
  table.insert(result, a[i])
 for i = 1, #b do
  table.insert(result, b[i])
 return result
end
> tbl1 = {"alpha", "beta", "gamma"}
> tbl2 = {"delta", "epsilon", "zeta"}
> setmetatable(tbl1, mt)
> setmetatable(tbl2, mt)
> add_test = tbl1 + tbl2
> print(#add_test)
6
> for i = 1, #add_test do print(i, addtest[i]) end
1, alpha
2, beta
3, gamma
4, delta
5, epsilon
6, zeta
mul, div, sub, unm的操作方式也比較類似, 執行自定義的運算過程, 最後返回一個table.
////////////////////////////////////////////////////////////////////////// 
__tostring
如前所述, __tostring函式用於處理當table作為tostring()函式之參數時的行為. 很顯然, __tostring函式應該返回一個字符串. tostring函式需要一個參數(代表作為參數的table).
function mt.__tostring(tbl)
 local result = "{"
 
 for i = 1, #tbl do
  if i > 1 then
   result = result .. ", "
  end
  result = result .. tostring(tbl[i])
 end
 result = result .. "}"
 return result
end
> tbl1 = {"alpha", "beta", "gamma"}
> tbl2 = {"delta", "epsilon", "zeta"}
> setmetatable(tbl1, mt)
> setmetatable(tbl2, mt)
> print(tbl1)
{alpha, beta, gamma}
> print(tbl2)
{delta, epsilon, zeta}
////////////////////////////////////////////////////////////////////////// 
__index
當table中不存在的key值被訪問時, 系統將返回nil(空). 在一些情況下, 可能希望返回更有意義的內容. 在這種情況下, 可以通過__index來達到這個效果.
__index的行為如下: 在table中不存在的key值被訪問時呼叫__index, 例如: value = tbl.undefined, 此時將呼叫__index(如果其存在, 否則返回nil).
__index可以指向另一個table; 在這種情況下, 當其他語句訪問原table中不存在的key值時, 會得到__index所指向的table中同名的key值. 例如:
deDE_races = {
 ["Night elf"] = "Nachtelf"
}
mt = {}
setmetatable(deDE_races, mt)
enUS_races = {
 ["Human"] = "Human",
 ["Night elf"] = "Night elf",
}
mt.__index = enUS_races
> print(deDE_races["Night elf"])
Nachtelf
> print(deDE_races["Human"])
Human
當deDE_races表中不存在的key值(deDE_races.Human)被訪問時, 返回了__index所指向的另一個table中同名的key值(enUS_races.Human).
這是一種很方便的定義默認值的方法. 若將所有默認值在一個default_tbl中定義好, 以後其他同類型的table全部以__index掛接default_tbl, 則無需在每個table中重新設置所有key的默認值; 當未設定的某key被訪問時, __index將指導其自動返回預設表中的key值.
除了掛接到另一個table以返回“默認值”外, __index還可以指向一個函數, 以處理包括返回錯誤信息等更加複雜的工作. 在此種情況下, __index必須攜帶兩個參數, 依次為被訪問的table及被訪問的key值, 並必須返回一個值. 例如, 可以寫一個這樣的函數來產生錯誤信息:
function mt.__index(tbl, key)
 print("ERROR: Attempt to access undefined key '"..key.."' in " .. tostring(tbl))
 return nil
end
//////////////////////////////////////////////////////////////////////////
__newindex
在一些特定的情況下, 可能需要限制table中的key進行賦值的行為(包括產生新key值), 可以用__newindex來處理這個問題. __newindex攜帶三個參數: 在賦值行為中的table, key及賦予的值(value).
例如, 以下的函式可以防止在所掛接的table中建立banana這個key值:
function mt.__newindex(tbl, key, value)
 if key == "banana" then
  error("ERROR: Cannot set a protected key")
 else
  rawset(tbl, key, value)
 end
end
這樣, 當試圖建立這個key值時, 會得到如下的報錯信息:
> tbl1.banana = "yellow"
stdin: 3: ERROR: Cannot set a protected key
stack traceback:
    [C]: in function 'error'
    stdin:3: in function
    stdin:1: in main chunk
    [C]: ?
> print(tbl1.banana)
nil
這裡用到了rawset函數, 與其相關的還有rawget函數, 這兩個函數會在忽略所掛接的metatable的前提下, 存取table中的key值, 其語法分別為:
value = rawget(tbl, key)
rawset(tbl, key, value)
 //////////////////////////////////////////////////////////////////////////
多重返回值
在魔獸世界中, 有一些時候我們會需要一個函數返回超過一個值. 看下面的例子:
顏色在界面編程中是一項很重要的數值. 常見的顏色方法是用六位的十六進制數字, 每兩位依次表示三原色中一種顏色在最終混合而成的顏色中所擁有的強度,從0(00)到255(FF). 在有些情況下, 可能會分別需要三原色每種色的強度數值. 在獲取了六位十六進制數字後, 我們可以自己寫一個函式返回分開的三種顏色的強度值.
例如說, 顏色#99CCFF可以被表示為(0.6, 0.8, 1.0), 其中1.0代表最大強度(255).
要設計一個這樣的函式, 首先要涉及到以下的兩個函式: string.sub()以及tonumber().
string.sub()函式用於取得給定字符串的子字符串, 而tonumber()可以將字符串以給定的進制轉化為10進制數字.
這樣, 我們得到下面的函式代碼:
function ConvertHexToRGB(hex)
 local red = string.sub(hex, 1, 2)
 local green = string.sub(hex, 3, 4)
 local blue = string.sub(hex, 5, 6)
 -- Lua無敵的變量類型可變的優勢在這裡完美的體現了...
 red = tonumber(red, 16) / 255
 green = tonumber(green, 16) / 255
 blue = tonumber(blue, 16) / 255
 
 return red, green, blue -- 返回多重值的語句寫法
end
函數的使用結果如下:
> print(ConvertHexToRGB("FFCC99"))
1, 0.8, 0.6
> print(ConvertHexToRGB("FFFFFF"))
1, 1, 1
> print(ConvertHexTORGB("000000"))
0, 0, 0
如果需要將這個函數返回的值賦給其他的變量, 可以這樣寫:
red, blue, green = ConvertHexToRGB("FFCC99")
要注意的是, 等號右邊也可以有多個數值, 它們看起來應該是和左邊一一對應賦值的; 但包括返回多重值的函數時, 情況將發生變化.
red, blue, green, alpha = ConvertHexToRGB("FFCC99"), "yet", "another", "argument"
在這個語句執行之後, 並不表示alpha將得到"yet", 而多餘的兩個字符串值將被拋棄; 出人意料的, 只有red正確地接收了函數返回的值, 而blue, green, alpha則分別接收了"yet", "another"和"argument"三個字符串.
用具有多重返回值的函數作為參數時, 也有類似的情況:
> print(ConvertHexToRGB("FFFFFF"))
1, 1, 1
> print(ConvertHexToRGB("FFFFFF"), "SomeOtherArgument")
1, SomeOtherArgument
出現這個情況的理由是這樣的: 因為一些技術上的原因, 在Lua中, 類似的情況下一個具有多重返回值的函數必須位於參數列表的末尾, 否則它將只返回第一個值. 在第二個語句中, ConvertHexToRGB("FFFFFF")後面還有其他的參數, 於是它只返回了第一個值.
附注: 如果特意只需要第一個返回值, 可以用下面的方法, 即: 為函數語句多增添一對括號:
> print((ConvertHexToRGB("FFFFFF")))
1
有很多魔獸世界中的函式都具有多重返回值, 例如函式GetRaidRosterInfo()以玩家在raid中的編號為參數, 並將返回包括玩家姓名、團隊職位(團長、團長助手或一般成員)、所在小隊、角色等級、職業、目前所在地區、是否在線、是否死亡等等訊息.
一個函式可能會返回如此多的值, 但並不代表只為了利用其中的某一個值(不是第一個被返回的值), 就必須列一排的變量把結果全部接收下來. 當然在函式返回的值並不是太多的時候, 還是可以用這樣的方法; 因為Lua並沒有限制一個變量的類型(在賦值的同時就自動更換了), 可以用一些不太會用到的名字命名一個回收箱變量, 然後把不需要的值都丟到裡面去. 這種做法被稱作虛擬變量法(dummy variable). 例如, 在下面的語句中, 用了單下劃線這樣正常情況下不會用到的名字來命名一個虛變量, 用它來丟棄不需要的數值.
local _, g = ConvertHexToRGB("FFFFFF")
如同預期, g得到了函式返回的對應綠色(green)的顏色強度值, 而排在其之前被返回的紅色強度值則被丟棄到不會用到的變量"_"當中去了.
明顯的, 如果函式有了如同前面所說的數量龐大的返回值, 這種方法就一點都不好用了; 你需要浪費存儲空間和語句來建立一大堆名字不同的垃圾箱, 只是為了丟棄不需要的變量. Lua提供了一個更優秀的解決方案: 函式select().
select()函式接收一個參數n, 用來指定一個起始點; 然後select函式將返回指定的多重返回值序列中起點開始到序列末尾為止的部份. 如只需要起始處的元素, 則再對函數語句增加一對括號即可.
下面的代碼簡單地體現了select函式的功能.
> print(select(1, "alpha", "beta", "gamma"))
alpha, beta, gamma
> print(select(2, "alpha", "beta", "gamma"))
beta, gamma
之前提過, 函數可以有不確定個數的參數(筆記: Lua基礎: 函數, 控制流: "特別地, Lua支持不確定個數的參數列表..."), select()也可以用來幫助處理這裡的參數列表.
select()函數的第一個參數, 除了是返回列表的起始點外, 還可以是字符"#". 在這種情況下, 它返回後續參數列表的長度:
> print(select("#", "alpha", "beta", "gamma"))
3
可以用這樣的方法產生類似foreach的功能:
function printargs(...)
 local num_args = select("#", ...)
 for i = 1, num_args do
  local arg = select(i, ...)
  print(i, arg)
 end
end
> printargs("alpha", "beta", "gamma")
1, alpha
2, beta
3, gamma
這個功能比之前的foreach實現方式(for i=1, #tbl do)有一個優點: 可以在參數列表中放入空值(nil).
用#tbl實現時, 如果遇到nil元素, 將導致循環中止, 但用select語句實現的話, 循環不會中止.
function printargs2(...)
 local tbl = {...}
 for i = 1, #tbl do
  print(i, tbl[i])
 end
end
> printargs2("alpha", nil, "gamma")
1, alpha
> printargs("alpha", nil, "gamma")
1, alpha
2, nil
3, gamma
 //////////////////////////////////////////////////////////////////////////

迭代器
目前為止提到的遍歷功能都有一個共同的缺陷, 即: 它們都只支持table中標準array結構的部份, 而對以string為key的部份(根據它的實現方式, 也可以稱為Hash部份)是無能為力的. 這並不代表不能把這一部份納入遍歷當中; 只是需要換另一種方式來寫. 這樣的功能需要內建的迭代器(iterator)函式來予以實現.
迭代器是"一個對象, 可以讓程序編寫人員遍歷一個集合中的所有元素, 不管這個元素技術上的實現方式如何".
(Wikipedia: An iterator is an object which allows a programmer to traverse through all elements of a collection, regardless of its specific implementation.)
實際使用時仍依賴for函數, 此時的for格式有所不同, 稱作generic for.
for val1, val2, val3, ... in <expression> do
 -- body of the for loop
end
其中在<expression>標籤處放置的是迭代表達式, 此表達式返回以下三個值:
在每個迭代循環中被呼叫的函數; 迭代循環的初態; 迭代變量的初始值.
例如, 迭代表達式ipairs()返回下面的內容:
> print(ipairs(table))
function: 0x300980, table: 0x3072c0, 0
> print(table)
table: 0x3072c0
幸運的, 除非需要自己寫迭代器, 這三個值一般不需要深入了解. 系統內部會自動處理並生成所需的迭代器.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
ipairs()
ipairs()函數用於遍歷table中的數組部分.
> tbl = {"alpha", "beta", "gamma"}
> for indx, value in ipairs(tbl) do
>> print(idx, value)
>> end
1, alpha
2, beta
3, gamma
ipairs()函數以一個table為參數並返回for循環遍歷table所需的全部信息. 每次呼叫迭代函數時均獲得一個元素的索引值, 及元素本身.
for後面的變量名稱可以任意, 呼叫迭代函數所返回的值將依序填充到變量列表中的變量中去. 這些變量的作用範圍僅限於for循環體.
 //////////////////////////////////////////////////////////////////////////

pairs()
pairs()函數基本和ipairs()函數用法相同, 區別在於pairs()可以遍歷整個table, 即包括數組及非數組部分.
> tbl = {"alpha", "beta", ["one"] = "uno", ["two"] = "dos"}
> for key, value in pairs(tbl) do
>> print(key, value)
>> end
1, alpha
2, beta
one, uno
two, dos
受到哈希表實現方式的影響, 此遍歷過程的順序和元素加入表的順序是無關的. 上述結果與輸入順序相同只是巧合.
注意: pairs()函數在遍歷時會呼叫table內建的next()函數, 如果在pairs()對table進行遍歷的過程中對table進行加值操作, 將使next函數不能正常工作, 並使遍歷終止或提前結束.
利用pairs()的遍歷還可以清空一個table, 如:
for key,value in pairs(tbl) do
   tbl[key] = nil
end
 
string.gmatch()
string.gmatch()類似是Lua中的"正則表達式配對", 可以用來配對並獲取原始字符串中的部分.
> for word in string.gmatch("These are some words", "%S+") do
>> print(word)
>> end
These
are
some
words
> for char in string.gmatch("Hello!", ".") do
>> print(char)
>> end
H
e
l
l
o
!
詳細的配對規則到下一篇筆記的時候再寫.


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 createps 的頭像
    createps

    遊戲人生 人生遊戲

    createps 發表在 痞客邦 留言(0) 人氣()