[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
 如果你有仔細看,會發現上面有一條等式裡使用了「==」,這跟一個等於 = 差在哪呢?這就是我們接下來的主題:關係運算子。
    • 關係(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我們都在上次看過了,就是連接字串的「..」和計算字串、table 長度的「#」。
 註2:Lua 5.3 裡新增了位元運算子(Bitwise Operator)。
    • 運算子的優先級
 就像我們的四則運算有先乘除後加減一樣,Lua 中的運算子們也有類似這樣的順序,以下按照先後順序列出運算子(即越前面越先做運算):
^
not    #    - (負)
*    /    %
+    -
..
<    >    <=    >=    ~=    ==
and
or
 同一列代表有相同優先級,另外,除了 ^ 和 .. 這兩個是右邊先做(右關聯)之外,其他二元運算子都是左邊先做(左關聯)

  • 函式呼叫
 當我們呼叫一個函式時,通常會得到所謂的回傳值,像是 tonumber() 會給我們一個轉換好的 number,而 print() 則是回傳 nil,或我們也可以說他沒有回傳值。
 在這裡我想再介紹一個好用的函式: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)
 讓我們回顧一下上次提到的 table 用法:
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

留言

這個網誌中的熱門文章

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

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

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