【Lua 筆記】作用域 / 函數 - part 7
【Lua 筆記】作用域 / 函數 - part 7
由於有款遊戲叫做 CSO(Counter-Strike Online),內建模式創世者模式(Studio)新增使用 Lua 及其遊戲的 API,所以突發奇想製作這個筆記。
這個筆記會在一開始先著重純粹的程式設計自學,在最後的章節才會與 CSO 遊戲 API 進行應用。
作用域(scope)
作用域(scope),表示一個變數或函數等能夠作用的範圍,什麼意思呢?請看以下例子:
1 | a = 10 -- 這是全域變數 |
lua 當中的變數,沒有在前面加上 local 以外,全部都是「全域變數」,你可以把全域變數想像成是一個到哪裡都可以用的變數。
局域變數就只限縮於”目前”的範圍,什麼意思呢?也就是在變數前面宣告了 local 這個關鍵字,就只有這整個文件可以用這個變數而已,其他的文件不能存取到它的值。(這只是粗淺的解釋,實際上看的是他目前所在的作用域範圍)
具體是何種”目前”的範圍,我們在說明函數的時候稍待說明一下。
函數(function)
函數(function),又被稱為副程式、函式,我們在之前有稍微說明過它的概念,忘記的請至 part 1 的資料型態重看一遍。
總之函數它就是一台有功能的機器,輸入東西之後,在裡面運轉的機器(功能),運轉完後會輸出東西給我們。
以下是 lua 對函數的定義:
1 | optional_function_scope function function_name(argument1, argument2, argument3..., argumentn) |
- optional_function_scope : 可以指定這個函數是全域還是局域,全域的話就不用寫,局域請打上 local 關鍵字。
- function_name : 指定函數名稱。
- argument1, argument2, argument3…, argumentn : 表示一個函數的參數,多個參數以逗號隔開,函數也可以不帶參數。
- function_body : 函數體,函數中需要執行的程式碼區塊。
- result_params_comma_separated : 函數當中的回傳值,Lua 的函數可以回傳多個值,每個值以逗號隔開。
:::danger
註:return 一旦觸發後,函數裡面 return 後續的程式碼就不會繼續執行,所以 return 可以表示回傳後結束。
:::
函數當中的變數
前面我們談到作用域的部分,用函數解釋是最為明顯的,請看以下例子:
1 | a = 10 -- 設定全域變數 |
在函數外面的部分,a 被設定是全域變數,a = 10。
函數裡面又設定一個局域變數,也叫做 a = 15。
我們要使用函數呢,就直接寫它的函數名稱再加上小括號就好囉,然後這個我們稱之為呼叫(call)函數。
執行結果,發現 a 的值竟然沒有被改變,理應來說兩個都要是 15 才對啊!
為什麼?這就是作用域規則啦~因為函數裡面本身也是一種局域的作用域,函數以外的就拿不到函數內的東西囉。
或是可以這樣想:函數外的全域變數,與函數內的局域變數兩者是截然不同的變數。
換個方式,如果我們這樣寫:
1 | a = 10 |
可以發現,輸出結果就變兩個都是 15 了。把 local 拿掉,此時的 a 就不再是兩個截然不同的變數了,就好像把散掉的分身結合再一起一樣。
此時函數內調用的就是 a 這個「全域變數」。
多重回傳值
範例來源:Lua 函数 | 菜鸟教程
1 | function maximum (a) |
執行結果:
1 | 23 3 |
以上的程式碼可以從一串數字當中,從陣列索引 1 開始,尋找出最大值並且回傳,還能夠回傳該最大值在陣列當中的索引值。
不定長參數(可變參數)
Lua 中使用三點 ... 表示函數有可變的參數,意思就是可以輸入很多個參數進去。
以下是一個範例(Lua 函数 | 菜鸟教程):
1 | function add(...) |
上面的意思概括來說,就是呼叫 add 函數時,能夠輸入很多不限長度的參數進去,像是上面輸入 3,4,5,6,7,進入函數裡面。
這支函數主要計算由不定長參數組成的陣列中,所有元素的總和。
先用一個 temp 變數暫存(這裡稱為 s,sum 為總和的意思),之後在透過運算 s = s + v 不斷計算求得總和。
以下的範例(Lua 函数 | 菜鸟教程)當中,可以透過 select(“#”,…) 來取得不定長參數的數量:
1 | function average(...) |
輸出結果:
1 | 總共傳入 6 個數 |
有時候我們需要一些固定的參數,再來加上不定長參數,以下是一個應用範例:
1 | function func(fixedParam, ...) -- fixedParam 為固定參數, ... 是不定長參數 |
輸出結果:
1 | 固定參數 : 固定值 |
然後再來介紹 select 的另一個用法:
:::infoselect(n, …) 用於回傳從起點 n 開始到結束位置的所有參數列表。
:::
呼叫 select 時,必須傳入一個固定實參 selector (選擇開關) 和一系列變長參數。如果 selector 為數字 n,那麼 select 傳回參數列表中從索引 n 開始到結束位置的所有參數列表,否則只能為字串 #,這樣 select 會傳回變長參數的總數。
上面這段話擷取自:Lua 函数 | 菜鸟教程
解釋一下:
- 固定實參(Fixed Argument):固定實參是指在函數呼叫時,必須傳遞的參數。這些參數的數量和順序是固定的,函數定義時就已經確定。
- 變長參數(Variable-Length Argument):也就是不定長參數。變長參數是指在函數呼叫時,可以傳遞任意數量的參數。這些參數的數量和順序在函數定義時是不確定的。Lua 使用三個點 (…) 來表示變長參數。
看起來很複雜,很不容易懂對吧?以下這樣解釋或許會比較清楚:
:::warning
select 函數:
它的第一個參數是固定實參 selector,後面的參數是變長參數。select( 這裡是第一個參數 , …)
如果 selector 是一個數字 n,則 select 會回傳從第 n 個變長參數開始到最後一個變長參數的列表。
如果 selector 是字串 #(”#”),那麼 select 會返回變長參數的總數。
:::
還是不清楚的話,讓我們看個具體的例子:
1 | function func(...) |
輸出結果:
1 | a b c d |
練習一下:請用 lua 寫出一支程式碼,運用本篇的概念(不定長參數、select 函數運用),寫出如下程式碼一樣的輸出結果。
1 | a = {1,2,3,4} |
輸出結果:
1 | arg 1 |
:::spoiler 點左邊三角形, 看解答跟詳解
::: info
程式碼源自:Lua 函数 | 菜鸟教程
1 | do |
解釋:
do ... end 表示創建一個局部作用域,避免影響到外部程式碼的運作。foo 函數和任何在區域內定義的局部變數在 do ... end 區塊結束後都不會影響到外部的程式碼。
第三行:用 for 迴圈來遍歷所有傳遞給 foo 函數的參數。select(‘#’, …) 回傳傳遞給函數的參數個數。
第四行:定義局域變數 arg = select(i, …),用 select 函數來獲取第 i 個參數並將其指定給局部變量 arg。select(i, …) 回傳第 i 個參數的值。
註:直接 print 出 arg 跟用 print(select(i, …)) 是不一樣的,前者只會印出 select() 的第一個參數,後者從第 i 個參數到最後一個參數全印。
:::
總結
作用域(scope),表示一個變數或函數等能夠作用的範圍。
分為全域作用域、局域作用域,當然也分成全域變數、局域變數,抑或是全域函數、局域函數等。
使用 do ... end 可建立一個局域作用域。像這樣:
1 | a = 10 |
以下是 lua 對函數的定義:
1 | optional_function_scope function function_name(argument1, argument2, argument3..., argumentn) |
- optional_function_scope : 可以指定這個函數是全域還是局域,全域的話就不用寫,局域請打上 local 關鍵字。
- function_name : 指定函數名稱。
- argument1, argument2, argument3…, argumentn : 表示一個函數的參數,多個參數以逗號隔開,函數也可以不帶參數。
- function_body : 函數體,函數中需要執行的程式碼區塊。
- result_params_comma_separated : 函數當中的回傳值,Lua 的函數可以回傳多個值,每個值以逗號隔開。
:::danger
註:return 一旦觸發後,函數裡面 return 後續的程式碼就不會繼續執行,所以 return 可以表示回傳後結束。
:::
Lua 中使用三點 ... 表示函數有可變的參數,意思就是可以輸入很多個參數進去。(不定長參數或稱變長參數)
select (index, ···)
:::warning
select 函數:
它的第一個參數是固定實參 selector,後面的參數是變長參數。select( 這裡是第一個參數 , …)
如果 selector 是一個數字 n,則 select 會回傳從第 n 個變長參數開始到最後一個變長參數的列表。
如果 selector 是字串 #(”#”),那麼 select 會返回變長參數的總數。
:::



