[Lua] 運算式-Lua 新手村 (3)
在上一章我們看到了很多種型別以及他們的值,但是我們該如何運用他們呢?一個方法就是透過即將要介紹的各種運算子來組合成更複雜的值,也就是所謂的「運算式」。
- 運算式 Expression
運算式又稱作表示式,他代表了值,在 Lua 中,運算式可以是簡單的值、變數、包含運算符號的式子,以及函式的呼叫,甚至是函式定義和 table 的建構,在這篇文章裡,我們會提到上述除了函式定義之外的運算式類型。
- 簡單的值
這個分類是我為了讓讀者容易理解而使用的分類,只要是上一篇文提到的值都算在此類。舉凡 nil、true、false、各種 number,以及字串實字(Literal),也就是由單雙引號或雙中括號包起來的字串。
- 變數
- 運算子 Operator
運算子也就是我們熟悉的運算符號,像是 +、-、*、/ 之類的,不過在 Lua 裡,還有更多種運算子。
- 算數(Arithmetic)運算子
+ (加) - (減) * (乘) / (除) % (模除) ^ (指數)
- (負數)
我們來簡單看看要怎麼使用他們:12 + 75
87
36 - 19
17
3 * 5 * 7
105
9 / 3
3
2 / 7
0.2857142857142857
8 % 5
3
-79 % 4
1
6 ^ 2
36
(-4) ^ 3
-64
注意「%」這個運算子的作用,他是由下面這條等式所定義的,其中 math.floor(x) 這個函式可以取得不大於 x 的最大整數:
a % b == a - math.floor(a/b)*b
特別的是,% 也可以用在整數以外的運算:
math.pi
3.1415926535...
math.pi % 1
0.1415926535...
math.pi % 0.01
0.0015926535...
p = math.pi
print(p - p % 0.01)
3.14
如果你有仔細看,會發現上面有一條等式裡使用了「==」,這跟一個等於 = 差在哪呢?這就是我們接下來的主題:關係運算子。
不過,「參考」又是什麼意思?還記得我們在上一篇提到,變數像個紙箱,然後「指定」是把值放進去紙箱嗎?但是對於物件(如 table)來說,放入變數的並不是物件本身,而是他的「位址」,也就像是門牌號碼一樣;而每當我們寫下 {} 時,就建構1了一個新的 table,其位址也會與其他已經存在的 table 不同,所以我們在對 t1 和 t2 進行比較時,其實是在比較不同 table 的門牌號碼,也就是他們的位址,所以結果的確會不一樣。
註1:在下面會更進一步說明 table 的建構。
- 關係(Relational)運算子
< (小於) > (大於) <= (小於或等於) >= (大於或等於) == (相等) ~= (不相等)
其中注意到相等要用兩個等號「==」,如果只有一個等號,那會變成變數的「指定」。
3 < 6
true
9 > 12
false
8 <= 6
false
7 >= 7
true
5 == 9
false
2 == 2
true
4 ~= 7
true
另外,字串也可以比較,使用的是字典順序:'apple' > 'add'
true
'fork' < 'fog'
false
s = 'lua' -- 指定
s == 'lua' -- 比較
true
而物件類型(如 table)的比較,則是以參考(reference)來進行:
t1 = { 1, 2 } -- 1 號 table
t2 = { 1, 2 } -- 2 號 table
t3 = t1
t1 == t2
false
t1 == t3
true
可以看到 t1 跟 t2 雖然內容一樣,但卻被判定成不相等,因為兩個變數分別參考到 1 號及 2 號 table;而 t1 跟 t3 會相等是因為都參考到同一個 1 號 table。不過,「參考」又是什麼意思?還記得我們在上一篇提到,變數像個紙箱,然後「指定」是把值放進去紙箱嗎?但是對於物件(如 table)來說,放入變數的並不是物件本身,而是他的「位址」,也就像是門牌號碼一樣;而每當我們寫下 {} 時,就建構1了一個新的 table,其位址也會與其他已經存在的 table 不同,所以我們在對 t1 和 t2 進行比較時,其實是在比較不同 table 的門牌號碼,也就是他們的位址,所以結果的確會不一樣。
註1:在下面會更進一步說明 table 的建構。
- 邏輯(Logical)運算子
and (且) or (或) not (非)
回想上一篇文提到,任何值都可以被視為條件式,這裡一樣適用。and 的情況是:如果前面的值相當於條件假則回傳前面的值,不然就回傳後面的值;而 or 的情況則是:如果前面的值是條件真就回傳前面的值,不則回傳後面的值;最後 not 很簡單:他會把條件真換成 false,條件假換成 true。15 and 7 -- 都是條件真
7
nil and 34 -- nil 是條件假
nil
6 and false -- 6 是條件真
false
15 or 7
15
false or 18
18
false or nil
nil
not 8 -- 8 是條件真
false
not nil
true
有一些 Lua 的慣用用法使用了這些運算子組成:
x = x or v
這行的意思是,如果 x 是 nil 或 false 的話,就把 x 設定為 v,不然就維持原樣,這在函式裡檢查參數很有用。另一個慣用用法是:
result = condi and x or y
-- 相當於 result = (condi and x) or y
這比較複雜,但也很容易理解:如果 condi 為 true,則 result 的值為 x,否則為 y。但要注意:只有 x 不是 false 時結果才會正確。
- 其他運算子
註2:Lua 5.3 裡新增了位元運算子(Bitwise Operator)。
- 運算子的優先級
^
not # - (負)
* / %
+ -
..
< > <= >= ~= ==
and
or
同一列代表有相同優先級,另外,除了 ^ 和 .. 這兩個是右邊先做(右關聯)之外,其他二元運算子都是左邊先做(左關聯)。- 函式呼叫
在這裡我想再介紹一個好用的函式:io.read(),他可以取得使用者從標準輸入取得資料,一般來說就是鍵盤,他的用法如下:
line = io.read() -- 等同於 io.read('*l')
print(line)
num = io.read('*n')
print(num)
a, b = io.read('*n', '*n')
print(a, b)
其中 "*n" 代表讀入一個數字,"*l" 代表讀入一整行。- table 建構式(Constructor)
ary = {} -- 這是一個建構式
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
ary4 = { apple=50, egg=80, 10, 20, 30 } -- 我們也可以混用不同形式的建構式
print(ary4[1], ary4[2], ary4[3])
10 20 30
print(ary4['apple'], ary4['egg'])
50 80
淺顯易懂!我們還有更強大的用法:
ary5 = { +="plus", -="minus", 20="twenty" } -- 不讓我們用特殊符號或數字當索引!
[string "..."]:1: unexpected symbol near '+'
ary5 = { ["+"]="plus", ["-"]="minus", [20]="twenty" } -- 這樣就可以了
print(ary5['+'])
plus
print(ary5[20])
twenty
而且這個例子裡,我們跳過了索引 1~19,直接給 20 一個值,這也是合法的行為。
最後,稍微提一下:建構式裡的逗號都可以用分號取代,還有最後一個元素的後面其實可以加上一個逗號,如 { 1, 2, }。
這次的文章內容在概念上比較簡單,但是因為可以把很多值組合、運算成新的值,所以其實很強大!而關係、邏輯運算子將會是我們下一篇文的主題,所以也請一定要熟悉這些運算子的使用。
後記:這次的量還滿剛好的(自己講),而且我竟然沒有拖稿XD
留言
張貼留言