Lua 函數


【1】函數定義

Lua函數定義格式如下:

1 optional_function_scope function function_name(argument1, argument2, argument3..., argumentn)
2     function_body
3 return result_params_comma_separated
4 end

解析:

optional_function_scope: 該參數是可選的,指定函數是全局函數還是局部函數。

             未設置該參數默認為全局函數,如果你需要設置函數為局部函數需要使用關鍵字 local。

function_name: 指定函數名稱。

argument1, argument2, argument3..., argumentn: 函數參數,多個參數以逗號隔開,函數也可以不帶參數。

function_body: 函數體,函數中需要執行的代碼語句塊。

result_params_comma_separated: 函數返回值,Lua語言函數可以返回多個值,每個值以逗號隔開。

【2】Lua函數示例

函數示例

 1 --[[ 函數返回兩個值的最大值 --]]
 2 local function max(num1, num2)
 3    if (num1 > num2) then
 4       result = num1;
 5    else
 6       result = num2;
 7    end
 8 
 9    return result;
10 end
11 
12 -- 調用函數
13 print("兩值比較最大值為 (10, 4) :: ", max(10, 4))
14 print("兩值比較最大值為 (5, 6) :: ", max(5, 6))
15 
16 
17 --[[
18 兩值比較最大值為 (10, 4) :: 10
19 兩值比較最大值為 (5, 6) ::     6
20 ]]

【3】Lua函數特色

(1)將函數作為參數傳遞給函數。示例如下:

 1 --[[將函數作為參數傳遞給函數]]
 2 myprint = function(param)
 3    print("這是打印函數 - ##", param, "##")
 4 end
 5 
 6 function add(num1, num2, functionPrint)
 7    result = num1 + num2
 8    -- 調用傳遞的函數參數
 9    functionPrint(result)
10 end
11 
12 myprint(10)
13 -- myprint 函數作為參數傳遞
14 add(2, 5, myprint)
15 
16 --[[
17 這是打印函數 - ##    10    ##
18 這是打印函數 - ##    7    ##
19 ]]

(2)函數可以返回多個值

例1,代碼如下:

 1 --[[例如:string.find 其返回匹配串“開始和結束的下標”(如果不存在匹配串返回nil)]]
 2 s, e = string.find("http://www.baidu.com", "baidu")
 3 print(s, e)
 4 
 5 --[[執行結果
 6 12    16
 7 ]]
 8 
 9 s, e = string.find("http://www.google.com", "baidu")
10 print(s, e)
11 
12 --[[執行結果
13 nil    nil
14 ]]

例2,代碼如下:

 1 --[[return后列出要返回值的列表即可返回多值]]
 2 function maximum (a)
 3     local mi = 1             -- 最大值索引
 4     local m = a[mi]          -- 最大值
 5     for i,val in ipairs(a) do
 6        if val > m then
 7            mi = i
 8            m = val
 9        end
10     end
11     return m, mi
12 end
13 
14 print(maximum({8, 10, 23, 12, 5}))
15 
16 --[[執行結果
17 23    3
18 ]]

例3,特殊函數unpack,它可以接受一個數組作為參數,並從下標1開始返回該數組的所有元素:

1 print(unpack{10, 20, 30})  -- 10  20  30

(3)不定形參函數

3.1 自定義不定形參函數:Lua函數可以接收可變數目的參數

1 function add(...)
2 local s = 0
3   for i, v in ipairs{...} do   -- {...} 表示一個由所有變長參數構成的數組
4     s = s + v
5   end
6   return s
7 end
8 
9 print(add(3, 4, 5, 6, 7))  -- 25

3.2 將可變參數賦值給一個變量

 1 --[[將可變參數賦值給一個變量]]
 2 function average(...)
 3    result = 0
 4    local arg = {...}    -- arg 為一個表,局部變量
 5    for i,v in ipairs(arg) do
 6       result = result + v
 7    end
 8    print("總共傳入 " .. #arg .. " 個數")
 9    return result / #arg
10 end
11 
12 print("平均值為", average(10, 5, 3, 4, 5, 6));
13 
14 --[[執行結果
15 總共傳入 6 個數
16 平均值為    5.5
17 ]]

3.3 可以通過select("#", ...來獲取可變參數的數量)

 1 --[[可以通過 select("#",...) 來獲取可變參數的數量]]
 2 function average(...)
 3    result = 0
 4    local arg = {...}
 5    for i, v in ipairs(arg) do
 6       result = result + v
 7    end
 8    print("總共傳入 " .. select("#", ...) .. " 個數")
 9    return result / select("#", ...)
10 end
11 
12 print("平均值為", average(10, 5, 3, 4, 5, 6))
13 
14 --[[執行結果
15 總共傳入 6 個數
16 平均值為    5.5
17 ]]

3.4 需要幾個固定參數再加上可變參數時,固定參數必須放在可變參數之前

 1 function fwrite(fmt, ...)  -- 固定的參數fmt
 2     return io.write(string.format(fmt, ...))
 3 end
 4 
 5 fwrite("baidu\n")        -- fmt = "baidu", 沒有變長參數。
 6 fwrite("%d%d\n", 1, 2)   -- fmt = "%d%d", 變長參數為 1 和 2
 7 
 8 --[[執行結果
 9 baidu
10 12
11 ]]

3.5 遍歷變長參數時只需要使用{...},然而變長參數可能會包含一些nil,那么就需要用select函數來訪問變長參數。

[1] select('#', ...)返回可變參數的長度

[2] select(n, ...)用於訪問n到select('#', ....)的參數

 1 --[[
 2 遍歷變長參數的時候只需要使用{…},然而變長參數可能會包含一些nil,那么就可以用select函數來訪問變長參數了:select('#', …) 或者 select(n, …)
 3 select('#', …) 返回可變參數的長度
 4 select(n, …) 用於訪問 n 到 select('#',…) 的參數
 5 ]]
 6 
 7 do
 8     function foo(...)
 9         for i = 1, select('#', ...) do  --> 獲取參數總數
10             local arg = select(i, ...); --> 讀取參數
11             print("arg", arg);
12         end
13     end
14 
15     foo(11, 12, 13, 14);
16 end
17 
18 --[[執行結果
19 arg    11
20 arg    12
21 arg    13
22 arg    14
23 ]]

(4)具名實參

具名實參,即具體名稱的實參值。比如,字符串拷貝函數有兩個參數(src, dest),當我們記不清第一個參數是src還是dest時,可以在調用時明確指明具體實參值。

Lua語言中的rename接口,就是個很有代表性的例子。試着新建一個文件temp.lua,然后復制以下代碼段:

1 function rename(arg)
2     return os.rename(arg.old, arg.new)
3 end
4 
5 rename({old = "temp.lua", new = "temp1.lua"})
6 --rename({new = "new1.lua", old = "temp1.lua"})

注意觀察:執行結果,同目錄下會多一個temp1.lua文件

再把第六行的注釋去掉,然后執行結果:同目錄下會多一個new1.lua文件。

(5)待續

【4】深入函數

(1)閉合函數

[1.1] 在Lua中,函數是第一類值

如何理解這個“第一類值”?類比着學習,我們知道C語言的數據類型有整數、浮點數、枚舉、指針、數組、結構、共用體、void等等。

前面我們也學習了《Lua數據類型》包括nil、boolean、number、string、function等等。

即就是,在Lua中,函數可以存儲到變量(無論全局變量還是局部變量)中或table中,可以作為實參傳遞給其他函數,還可以作為其他函數的返回值。

先看最常見的這個求和函數定義:

1 function func(a, b) return a + b end

其等價於

1 func = function(a, b) return a + b end

兩種方式比較:

第一種方式,似乎更符合我們對函數的潛意識(傳統的)視覺印象規則。

第二種方式,更彰顯了Lua中函數作為“第一類值”的體現和范疇。

通過以上兩種方式的對比,我們可以更深入的理解Lua中函數的定義本質:

一個函數定義實質上是一條賦值語句,創建了一種類型為“函數”的值,且將其賦值於變量func。

可以驗證一下類型,如下所示:

1 func = function(a, b) return a + b end
2 print(func)            -- function: 02DF8FC8
3 print(type(func))      -- function
4 print(func(10, 20))    -- 30

在Lua中,將表達式“function(x) <body> end”視為一種函數的構造式,就像table的構造式{ }一樣。

將這種函數構造式的結果稱為一個“匿名函數”,雖然一般情況下,會將函數賦予全局變量,即給予其一個名稱。

但是,在某些特殊情況下,仍會需要直接用到匿名函數。

不要問我哪里需要用到,其實你肯定見過,比如sort函數,請看如下示例:

 1 students =
 2 {
 3     {name = "Wang", age = "18"},
 4     {name = "Qin", age = "20" },
 5     {name = "Li", age = "19"},
 6     {name = "Wei", age = "21"},
 7 }
 8 
 9 --[[ before sort ::]]
10 print("before sort ::")
11 for i = 1, #students do
12     print(i, students[i].name, students[i].age)
13 end
14 
15 table.sort(students, function(a, b) return (a.name > b.name) end)
16 
17 --[[ after sort ::]]
18 print("after sort ::")
19 for k, v in pairs(students) do
20     print(k, v.name, v.age)
21 end
22 
23 --[[執行結果
24 before sort ::
25 1    Wang    18
26 2    Qin    20
27 3    Li    19
28 4    Wei    21
29 after sort ::
30 1    Wei    21
31 2    Wang    18
32 3    Qin    20
33 4    Li    19
34 ]]

像sort這樣的函數,接受另一個函數作為實參,稱其是一個“高階函數”,是一種強大的編程機制。

 1 names = {"Peter", "Paul", "Mary"}
 2 grades = {Mary = 10, Paul = 7, Peter = 8}
 3 table.sort(names, function(n1, n2)
 4     return grades[n1] > grades[n2]
 5     end)
 6 
 7 for k, v in ipairs(names) do
 8     print(k, v)
 9 end
10 
11 --[[
12 1    Mary
13 2    Peter
14 3    Paul
15 ]]
16 
17 names = {"Peter", "Paul", "Mary"}
18 grades = {Mary = 10, Paul = 7, Peter = 8}
19 
20 function sortbygrade(names, grades)
21     table.sort(names, function(n1, n2)
22     return grades[n1] > grades[n2]
23     end)
24     return names
25 end
26 
27 for k, v in ipairs(sortbygrade(names, grades)) do
28     print(k, v)
29 end
30 
31 --[[
32 1    Mary
33 2    Peter
34 3    Paul
35 ]]
36 
37 --[[一般函數]]
38 function increCounter1()
39    local i =  0
40     i = i + 1
41     return i
42 end
43 
44 print(increCounter1)     -- function: 02A6B8F8
45 print(increCounter1())   -- 1
46 print(increCounter1())   -- 1
47 
48 --[[閉合函數]]
49 function increCounter2()
50    local i =  0
51    return function()
52             i = i + 1
53             return i
54         end
55 end
56 
57 print(increCounter2)    -- function: 02A6B738
58 print(increCounter2())  -- function: 02A6B918
59 
60 c1 = increCounter2()
61 print(c1())            -- 1
62 print(c1())            -- 2
63 c2 = increCounter2()
64 print(c2())            -- 1
65 print(c1())            -- 3
66 print(c2())            -- 2
67 
68 --[[閉合函數 局部變量]]
69 function increCounter3()
70    return function()
71         local i = 9
72         i = i + 1
73         return i
74     end
75 end
76 
77 print(increCounter3)    -- function: 02A6B6F8
78 print(increCounter3())  -- function: 02A6B7D8
79 
80 c3 = increCounter3()
81 print(c3())            -- 10
82 print(c3())            -- 10
83 c4 = increCounter3()
84 print(c4())            -- 10
85 print(c4())            -- 10
86 print(c4())            -- 10

匿名函數

(2)非全局函數

(3)正確尾調用

【5】總結

Good Good Study, Day Day Up.

順序 選擇 循環 總結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM