[Lua] 型別與值-Lua 新手村 (2)

接下來要談談 Lua 裡的資料型別還有所謂的「值」。由於 Lua 是個動態語言,所以變數本身並不帶有型別資訊。嗯?這是什麼意思?
  • 動態語言
 不過這篇文畢竟是新手村,所以不會講得太深入,有興趣的讀者可以自行深入了解。
 所謂的動態語言就是,同一個變數裡的資料可以現在是一個數字,等一下又是一個字串,這樣能夠動態地轉換型別的語言就叫做動態語言。不過剛剛提到的變數(Variable)跟型別(Type)到底是什麼?
  • 變數、型別與值
 簡單的說,變數就像一個紙箱,並沒有規定裡面要裝什麼,可以裝任何你想裝的物品,而「值(Value)」就是能被裝入變數這個箱子的物品,那麼這個物品的類型就是型別。
 舉個例子,如果我有一個紙箱(變數),然後我把香蕉裝進去,那麼這個香蕉就是「值」,而他的類型(型別)就是水果。而且,我可以之後把裡面的香蕉換成白菜,如此一來,值就變成了白菜,而型別就變成了蔬菜,不過紙箱(變數)還是同一個

 而在 Lua 中要把一個值給一個變數,只要這樣寫就好:
box = 1024
這行中間的等於「=」就是所謂的指定,也就是把右邊的值放到左邊的變數裡面,這裡的變數叫做「box」,而值是「」1024」
  • Lua 中的型別
 Lua 中的資料型別只有 8 種,分別是 nil(無)、boolean布林)、number數字)、string字串)、function函式)、table)、thread執行緒)、userdata使用者資料),接下來會大致講解每種型別。
    • nil
 nil 是一個很重要的型別,而且我們在上一篇也遇過他,代表著「什麼都沒有」。屬於 nil 這個型別的值只有一個,就是nil。
    • boolean、number、string
 boolean 型別是拿來表示條件1的型別,只有兩個值:true 和 false,但這不代表只有這兩個值可以拿去當作條件式使用:在 Lua 中,除了 false 和 nil 代表 false(條件的假)之外,其他值都是 true(條件的真),包括 0 和空字串。
 註1:關於條件,會在下幾篇文章進行更深入的解說。

 number 就是一般數值的型別,無論是整數或是有小數的浮點數,都屬於 number 這個型別,像是 7、120、0.56、3.14159 等等;另外,如果你需要或習慣的話,也可以使用科學記號形式的表示法:1.91e6、3.2e-4、6.02e+23。最後,如果數值太大超過了 Lua 可以儲存的範圍的話,會變成一個叫 math.huge 的值,把他 print 出來會寫做 inf。

 string 是代表字串的型別,所謂字串,很像一句話,就是一些字所組成的值,而且這句話可以只有一個字,或根本沒有字(空字串)。要怎麼表示字串呢?我們要使用雙引號或單引號來包住那句話,比如說:
"hello"    'wow'    "123"    "can't"
 注意上面最右邊的是值為 "123" 的字串,跟數字的 123 不一樣。那麼,什麼時候要用單引號、什麼時候又要用雙引號呢?其實大多數時候是可以互換的,只是如果字串裡面剛好有單引號的話,就要用雙引號包住,反之亦然,不過在同一支程式裡最好選用同一種引號,除非不得已。我們除了能放一般的字進去字串之外,還可以使用所謂的「跳脫序列(Escape sequences)」,像是最常用到的「換行」,他的寫法是 \n,我們可以用 ZeroBrane 的互動模式來試試看:
print("Hello\nWorld")
Hello
World
 還有其他情況會用到這類字元,比如說我們可以把引號寫成 \" 和 \',用這種引號的話,外面無論是用單引號或雙引號包起來都可以:
print("Hello \"Sky\" !")
Hello "Sky" !
 看起來反斜線(\)好像有特殊用途,如果我們的字串想要單純的反斜線,就要用 \\:
print("C:\\User\\user")
C:\User\user
 當然還有其他的跳脫序列,比如用位元的數值來指定,但我就不一一詳述了。
 如果我們想要在字串裡包含換行,除了剛剛的 \n 之外,還可以使用由兩個中括號包起來的多行字串,如此一來只要在中括號之間放入換行就可以了,值得注意的是,如果第一個字元就是換行的話會直接被忽略,這有助於對齊字串內容。請看以下範例:(這次是直接開一個檔案來寫程式,因為在互動模式中不易輸入多行字串)
mulStr = [[
this
is
multiline
string
and escape sequence \n
will be ignored.
]]
print(mulStr)
-- this
-- is
-- multiline
-- string
-- and escape sequence \n
-- will be ignored.
--
 可以看到的是,就算你在字串裡寫了 \n,也不會被當成換行來處理,而最後面的換行則是原本就有的。然而這樣有個問題,如果我們的多行字串裡本來就有 ]] 呢?這樣會被 Lua 當作結尾而提早結束這個字串。解決方法是在開頭和結尾的兩個中括號裡面加入等量的 =。
eqStr = [==[
a[b[c]]
hello ]=]
]==]
print(eqStr)
-- a[b[c]]
-- hello ]=]
--
 補充一點,多行註解也可以這樣處理。
    • number 與 string 間的轉換
 一般來說,如果分別有一個數字和字串想要做運算,那 Lua 通常會幫我們處理這些事:
print("123" + 100)
223
print("123" + "100")
223
print("10 + 12")
10 + 12
print(456 + "asd")
456321
print("123" .. 100)
123100
print(456 .. "321")
456321
 在 Lua 中,「+」一定是把兩個數字相加2,如果有任何一個是字串的話會先行轉換,而在後半段我們可以看到「..」這個運算符號,指的是把兩個字串連接在一起,如果有任何一邊是數字,則將其會轉成字串。
 如果想要明確的進行轉換,可以使用 tostring()、tonumber() 這兩個函式,這時候如果轉換失敗則會回傳 nil,對於數字轉成字串,還可以使用「..」把數字跟空字串接在一起。
 最後,如果在字串的前面加上「#」就可以得到字串的長度,而且一個中文字的長度會是 2 或 3,視編碼而定。
 註2:其實可以透過 metatable 來改變這樣的行為,不過這已經是很後面的主題了。

    • table
 Lua 可以說是由 table 所撐起來的語言,因為 table 是 Lua 裡唯一的原生資料結構。他重要到要花上好幾章來談一談,但畢竟這裡是新手村,所以只會碰到 table 的表面,其他的之後再說吧。
 首先,table 到底是什麼?他很像其他語言的陣列,可以把很多資料放在一起,並藉由索引(index)來存取,只不過他的索引從 1 開始;而且他的索引還可以是數值以外的值,如字串、boolean,甚至是 table 自己,但是,就只有 nil 不能當作索引。這樣索引不限於數值的陣列我們叫作「關聯陣列(Associative array)」,而不是數值的索引我們通常會叫他「鍵(key)」。
 另外,對於 table,我們不會把他叫做值,而是稱作「物件」。我們可以使用大括號來建立 table,並用中括號加索引取得裡面的資料:
ary = {}  -- 空 table
ary['sky'] = 123  -- 加一組資料進去
print(ary['sky'])
123
ary2 = {10, 20, 30, 40, 50}  -- 以數字做為索引
print(ary2[1], ary2[2], ary2[3])  -- 從 1 開始
10    20    30
ary3 = { apple=50, egg=80 }  -- 以字串做為索引
print(ary3['apple'], ary3['egg'])
50    80
 注意到我們在 ary3 建立時,大括號內的索引不用加引號,但之後在使用的時候就要。另外注意以下情況時引號的用法:
ary4 = { x=50, y=80 }  -- 以字串做為索引
print(ary4['x'])
50
k = "y"
print(ary4[k])  -- k 就是 "y"
y
print(ary4['k'])  -- 'k' 跟 k 不一樣
nil
print(ary4.x, ary4.y)  -- .x、.y 等同 ["x"]、["y"]
50    80
 然後我們也可以看到,當用不存在的索引的話會回傳 nil。這裡的最後,我想提一下「#」對 table 的用處,跟字串很像,可以取得 table 的長度,不過這個長度指的是:「從索引 1 開始往後數,連續幾個值不是 nil」,也就是說,這只對以數字為索引的 table 有用而已,而且中途也不能突然有個 nil3
ary5 = {1, 2, 3, 4, 5}  -- 以數字做為索引
print(#ary5)
5
ary6 = {1, 2, nil, nil, 5, 6}
print(#ary6)
2
 table 大概就講到這裡,以後會再提到。
 註3:有時候可以穿插一個 nil,但這樣的行為並不固定,應該要避免。
    • function
 函式是可以做一系列指令的物件(對,他也是物件),像是 print() 會幫我們把資料印到螢幕上,tostring()、tonumber() 會幫我們轉換型別。還有一個函式叫做 type(),會告訴我們某個值或物件是什麼型別的。我們會在後面有專屬函式的文章。
    • userdata、thread
 userdata 讓我們可以使用以 C 語言寫好的資料型別,而 thread 則主要是用在協程上。我們會在後面的章節講解 thread。

 這篇對雖然初學者來說很重要卻也不簡單,如果一下子記不起來,就先有個概念,以後有需要再回來翻就可以了。感謝大家的閱讀,我會盡量不棄坑的(笑)。

後記:不小心寫太多,新手村都不新手了XD

留言

這個網誌中的熱門文章

[C] 每天來點字串用法 (2) - strcpy()、strncpy()

[Python] *args 和 **kwargs 是什麼?一次搞懂它們!

[C] 每天來點字串用法 (5) - strcat()、strncat()