1-5、Lua函數

1-5、Lua函數


函數有兩種用途:1.完成指定的任務,這種情況下函數作爲調用語句使用;2.計算並返回值,這種情況下函數作爲賦值語句的表達式使用。
語法:

function func_name (arguments-list)
	statements-list;
end;

調用函數的時候,如果參數列表爲空,必須使用()表明是函數調用。

print(8*9, 9/8)
a = math.sin(3) + math.cos(10)
print(os.date())

上述規則有一個例外,當函數只有一個參數並且這個參數是字符串或者表構造的時候,()可有可無:

print "Hello World"		<-->		print("Hello World")
dofile 'a.lua'			<-->		dofile ('a.lua')
print [[a multi-line		<-->		print([[a multi-line
			message]]								message]])
f{x=10, y=20}				<-->		f({x=10, y=20})
type{}						<-->		type({})

Lua也提供了面向對象方式調用函數的語法,比如o:foo(x)與o.foo(o, x)是等價的,後面的章節會詳細介紹面向對象內容。
Lua使用的函數,既可是Lua編寫的,也可以是其他語言編寫的,對於Lua程序員,用什麼語言實現的函數使用起來都一樣。
Lua函數實參和形參的匹配與賦值語句類似,多餘部分被忽略,缺少部分用nil補足。

function f(a, b) return a or b end

CALL				PARAMETERS

f(3)				a=3, b=nil
f(3, 4)			a=3, b=4
f(3, 4, 5)			a=3, b=4	(5 is discarded)

5.1 多返回值

Lua函數可以返回多個結果值,比如string.find,其返回匹配串“開始和結束的下標”(如果不存在匹配串返回nil)。

s, e = string.find("hello Lua users", "Lua")
print(s, e)		--> 7	9

Lua函數中,在return後列出要返回的值得列表即可返回多值,如:

function maximum (a)
	local mi = 1				-- maximum index
	local m = a[mi]			-- maximum value
	for i,val in ipairs(a) do
		if val > m then
			mi = i
			m = val
		end
	end
	return m, mi
end

print(maximum({8,10,23,12,5}))		--> 23   3

Lua總是調整函數返回值的個數以適用調用環境,當作爲獨立的語句調用函數時,所有返回值將被忽略。假設有如下三個函數:

function foo0 () end						-- returns no results
function foo1 () return 'a' end			-- returns 1 result
function foo2 () return 'a','b' end	-- returns 2 results

第一,當作爲表達式調用函數時,有以下幾種情況:

    1. 當調用作爲表達式最後一個參數或者僅有一個參數時,根據變量個數函數儘可能多地返回多個值,不足補nil,超出捨去。
    1. 其他情況下,函數調用僅返回第一個值(如果沒有返回值爲nil)
x,y = foo2()				-- x='a', y='b'
x = foo2()					-- x='a', 'b' is discarded
x,y,z = 10,foo2()			-- x=10, y='a', z='b'

x,y = foo0()				-- x=nil, y=nil
x,y = foo1()				-- x='a', y=nil
x,y,z = foo2()			-- x='a', y='b', z=nil

x,y = foo2(), 20			-- x='a', y=20
x,y = foo0(), 20, 30		-- x='nil', y=20, 30 is discarded

第二,函數調用作爲函數參數被調用時,和多值賦值是相同。

print(foo0())				-->
print(foo1())				--> a
print(foo2())				--> a   b
print(foo2(), 1)			--> a   1
print(foo2() .. "x")		--> ax

第三,函數調用在表構造函數中初始化時,和多值賦值時相同。

a = {foo0()}				-- a = {}    (an empty table)
a = {foo1()}				-- a = {'a'}
a = {foo2()}				-- a = {'a', 'b'}

a = {foo0(), foo2(), 4}	-- a[1] = nil, a[2] = 'a', a[3] = 4

另外,return f()這種形式,則返回“f()的返回值”:

function foo (i)
	if i == 0 then return foo0()
	elseif i == 1 then return foo1()
	elseif i == 2 then return foo2()
	end
end

print(foo(1))			--> a
print(foo(2))			--> a  b
print(foo(0))			-- (no results)
print(foo(3))			-- (no results)

可以使用圓括號強制使調用返回一個值。

print((foo0()))		--> nil
print((foo1()))		--> a
print((foo2()))		--> a

一個return語句如果使用圓括號將返回值括起來也將導致返回一個值。
函數多值返回的特殊函數unpack,接受一個數組作爲輸入參數,返回數組的所有元素。unpack被用來實現範型調用機制,在C語言中可以使用函數指針調用可變的函數,可以聲明參數可變的函數,但不能兩者同時可變。在Lua中如果你想調用可變參數的可變函數只需要這樣:

f(unpack(a))
unpack返回a所有的元素作爲f()的參數
f = string.find
a = {"hello", "ll"}
print(f(unpack(a)))		--> 3  4

預定義的unpack函數是用C語言實現的,我們也可以用Lua來完成:

function unpack(t, i)
	i = i or 1
	if t[i] then
		return t[i], unpack(t, i + 1)
	end
end

5.2 可變參數

Lua函數可以接受可變數目的參數,和C語言類似在函數參數列表中使用三點(…)表示函數有可變的參數。Lua將函數的參數放在一個叫arg的表中,除了參數以外,arg表中還有一個域n表示參數的個數。
例如,我們可以重寫print函數:

printResult = ""

function print(...)
	for i,v in ipairs(arg) do
		printResult = printResult .. tostring(v) .. "\t"
	end
	printResult = printResult .. "\n"
end

有時候我們可能需要幾個固定參數加上可變參數

function g (a, b, ...) end

CALL				PARAMETERS

g(3)				a=3, b=nil, arg={n=0}
g(3, 4)			a=3, b=4, arg={n=0}
g(3, 4, 5, 8)		a=3, b=4, arg={5, 8; n=2}

如上面所示,Lua會將前面的實參傳給函數的固定參數,後面的實參放在arg表中。
舉個具體的例子,如果我們只想要string.find返回的第二個值。一個典型的方法是使用啞元(dummy variable,下劃線):

local _, x = string.find(s, p)
-- now use `x'
...

還可以利用可變參數聲明一個select函數:

function select (n, ...)
	return arg[n]
end

print(string.find("hello hello", " hel"))	--> 6  9
print(select(1, string.find("hello hello", " hel"))) --> 6
print(select(2, string.find("hello hello", " hel"))) --> 9

有時候需要將函數的可變參數傳遞給另外的函數調用,可以使用前面我們說過的unpack(arg)返回arg表所有的可變參數,Lua提供了一個文本格式化的函數string.format(類似C語言的sprintf函數):

function fwrite(fmt, ...)
	return io.write(string.format(fmt, unpack(arg)))
end

這個例子將文本格式化操作和寫操作組合爲一個函數。

5.3 命名參數

Lua的函數參數是和位置相關的,調用時實參會按順序依次傳給形參。有時候用名字指定參數是很有用的,比如rename函數用來給一個文件重命名,有時候我們我們記不清命名前後兩個參數的順序了:

-- invalid code
rename(old="temp.lua", new="temp1.lua")

上面這段代碼是無效的,Lua可以通過將所有的參數放在一個表中,把表作爲函數的唯一參數來實現上面這段僞代碼的功能。因爲Lua語法支持函數調用時實參可以是表的構造。
rename{old=“temp.lua”, new=“temp1.lua”}
根據這個想法我們重定義了rename:

function rename (arg)
	return os.rename(arg.old, arg.new)
end

當函數的參數很多的時候,這種函數參數的傳遞方式很方便的。例如GUI庫中創建窗體的函數有很多參數並且大部分參數是可選的,可以用下面這種方式:

w = Window {
	x=0, y=0, width=300, height=200,
	title = "Lua", background="blue",
	border = true
}

function Window (options)
	-- check mandatory options
	if type(options.title) ~= "string" then
		error("no title")
	elseif type(options.width) ~= "number" then
		error("no width")
	elseif type(options.height) ~= "number" then
		error("no height")
	end

	-- everything else is optional
	_Window(options.title,
		options.x or 0,			-- default value
		options.y or 0,			-- default value
		options.width, options.height,
		options.background or "white",	-- default
		options.border			-- default is false (nil)
	)
end
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章