在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
Lua支持面向对象,操作符为冒号‘:’。o:foo(x) <==> o.foo(o, x). Lua程序可以调用C语言或者Lua实现的函数。Lua基础库中的所有函数都是用C实现的。但这些细节对于lua程序员是透明的。调用一个用C实现的函数,和调用一个用Lua实现的函数,二者没有任何区别。
函数的参数跟局部变量一样,用传入的实参来初始化,多余的实参被丢弃,多余的形参初始化为nil。 count=0 function incCount(n) n=n or 1 count=count+n end incCount() print(count) incCount(5) print(count) 多返回值不同于常规函数,Lua的函数可以返回多个返回值。一些Lua中预定义的函数可以返回多个返回值。例如string.find函数,在string中匹配一个sub-string,string.find返回sub-string的起始位置和结束位置。利用多赋值语句来获取函数的多个返回值。
7 9 function maximum(a) local mi=1 --index local m=a[mi] 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})) ua会根据实际情况来使函数的返回值个数适应调用处的期望。 1)如果一个函数调用作为一条语句,所有的返回值都被丢弃 2)如果一个函数调用作为一个表达式的一部分时,返回值只保留第一个 3)只有当一个函数调用是一系列表达式中的最后一个元素(或仅有一个元素)时,才能获取它的所有返回值。这里的 “一系列表达式”在lua中表现为4中情况: 多重赋值,函数调用时传入的实参列表,,table的构造式,return语句中,。 在多重赋值中,若一个函数调用是最后的(或仅有的)一个表达式,那么Lua会保留尽可能多的返回值,用于 1. function foo0() end x,y=foo2() 2. 如果一个函数调用不是一系列表达式的最后一个元素,那么将只产生一个值: x,y=foo2(),"c"
当一个函数调用作为另一个函数调用的最后一个(或仅有的)实参时,第一个函数的所有返回值都将作为实参传入第二个函数。 print(foo2()) --a b print(foo2(),1) --a 1
3. table构造式可以完整接受一个函数调用的所有结果。 t={foo2()} 4. 最后一个情况是return语句,诸如return f()这样的语句,将返回f的所有返回值。
也可以将一个函数调用放入一对圆括号中,从而迫使它只返回以结果: print((foo2())) --a 请主语return语句后面的内容是不需要圆括号的,如果写成 关于多重返回值还有介绍一个特殊函数--unpack,他接受一个数组作为参数,并从下标1开始返回该数组的所有元素: print(unpack{10,20,30}) An important use for f(unpack(a))
The call to f = string.find a = {"hello", "ll"} then the call Although the predefined function unpack (t, i) i = i or 1 if t[i] ~= nil then return t[i], unpack(t, i + 1) end end The first time we call it, with a single argument,
Lua中的一些函数接受可变数量的参数,例如print函数。print函数是用C来实现的,但是我们也可以用Lua来实现变参函数。下面是一个示例: function add(...) local s=0 for i,v in ipairs{...} do s=s+v end return s end print(add(3,4,5)) 参数中的3个点(...)表示该函数可接受不同数量的实参,调用时3个点代表了实参。 表达式"..."的行为类似于一个具有多重返回值的函数,他返回的是当前函数的所有变长参数: 上例用第一个和第二个变长参数来初始化这两个局部变量,实际上,还可以通过变长参数来模拟Lua中的 如: 他只是简单的返回所有市场。这是一个“多值恒定式(multi-value identity)"函数。
具有参数named argu (lua函数调用特殊语法,当实参只有一个table时,可以省略圆括号)。 w = Window{ x=0, y=0, width=300, height=200, indow函数可以检查必须的参数,并且给可选参数赋予默认值等。假设_Window函数可以用来创建一个新窗口,但是它必须要全部的参数。那我们就可以重新定义一个Window函数如下: 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
二、深入函数: 在Lua中有一个容易混淆的概念是,函数与所有其他值一样都是匿名的,即他们都没有名称。当讨论一个函数名时(例如print)。实际上是在讨论持有某函数的变量,这与其他变量持有各种值一个道理,下面这个实例足以说明 a = { p = print }
function foo(x) return 2 * x end 这只是一种所谓的”语法糖“而已,实际等价于: foo = function(x) return 2 * x end
因此,一个函数定义实际上就是一条语句(更准备说是赋值语句)。这条语句创建了一种类型为”函数“的值。可以将表达式"function(x)<body> end"视为一种函数的构造式,就像table的构造式{}一样。将这种函数构造式的结果称为一个”匿名函数”。 下面的示例显示了匿名函数的方便性,它的使用方式有些类似于Java中的匿名类,如: 像sort这样的函数,接受另一个参数作为实参的,称其是一个”高阶函数“,高阶函数是一种 强大的编程机制,应用匿名函数来创建高阶函数所需的实参则可以带来更大的灵活性。但请记住,高阶函数并没有什么特权。Lua强调将函数视为”first-class valeu",所以,高阶函数只是基于该观点的应用体现而已。
function newCounter() local i = 0 return function() --匿名函数 i = i + 1 return i end end c1 = newCounter() print("The return value of first call is " .. c1()) print("The return value of second call is " .. c1()) --输出结果为: --The return value of first call is 1 --The return value of second call is 2 简单来讲,一个closure就是一个函数加上该函数所需访问的所有“非局部变量”。 在上面的示例中,我们将newCounter()函数称为闭包函数。其函数体内的局部变量i被称为"非局部变量",和普通局部变量不同的是该变量被newCounter函数体内的匿名函数访问并操作。再有就是在函数newCounter返回后,其值仍然被保留并可用于下一次计算。再看一下下面的调用方式。 在上面的示例中,我们将newCounter()函数称为闭包函数。其函数体内的局部变量i被称为"非局部变量",和普通局部变量不同的是该变量被newCounter函数体内的匿名函数访问并操作。再有就是在函数newCounter返回后,其值仍然被保留并可用于下一次计算。再看一下下面的调用方式。 function newCounter() local i = 0 return function() --匿名函数 i = i + 1 return i end end c1 = newCounter() c2 = newCounter() print("The return value of first call with c1 is " .. c1()) print("The return value of first call with c2 is " .. c2()) print("The return value of second call with c1 is " .. c1()) --输出结果为: --The return value of first call with c1 is 1 --The return value of first call with c2 is 1 --The return value of second call with c1 is 2
由此可以推出,Lua每次在给新的闭包变量赋值时,都会让不同的闭包变量拥有独立的"非局部变量"。下面的示例将给出基于闭包的更为通用性的用法: do --这里将原有的文件打开函数赋值给"私有变量"oldOpen,该变量在块外无法访问。 local oldOpen = io.open --新增一个匿名函数,用于判断本次文件打开操作的合法性。 local access_OK = function(filename,mode) <检查访问权限> end --将原有的io.open函数变量指向新的函数,同时在新函数中调用老函数以完成真正的打开操作。 io.open = function(filename,mode) if access_OK(filename,mode) then return oldOpen(filename,mode) else return nil,"Access denied" end end end 这个示例的精彩之处在于,经过重新定义后,一个程序就只能通过新的受限版本来调用原来那个未受限的open函数了。示例将原来不安全的版本保存到closure的一个私有变量中。从而使得外部再也无法直接访问原来的版本了。
do 对于这种局部函数,Lua还提供另外一种更为简洁的定义方式,如: 3. 正确的尾调用: 在之前提到了,一条“尾调用”就好比是一条“goto语句”。因此在Lua中”尾调用“的一大应用就是编写”状态机(state machine)"。这种程序通常以一个函数来表示一个的状态,改变状态就是goto到另一个特定的函数。举一个简单的迷宫游戏的例子来说明这个问题。例如,一个迷宫有几间房间,每间房间中最多有东南西北4扇门。用户在每一步移动中都需要输入一个移动的方向。如果在某个方向上有门,那么用户可以进入相应的房间;不然,程序就打印一条警告。游戏目标是让用户从最初的房间走到最终的房间。 这个游戏就是一种典型的状态机,其中当前房间是一种状态。可以将迷宫中的每间房间实现为一个函数,并使用”尾调用“来实现从一间房间移动到另一间房间。在以下代码中,实现一个具有4间房间的迷宫: function room1 () local move = io.read() if move == "south" then return room3() elseif move == "east" then return room2() else print("invalid move") return room1() -- stay in the same room end end function room2 () local move = io.read() if move == "south" then return room4() elseif move == "west" then return room1() else print("invalid move") return room2() end end function room3 () local move = io.read() if move == "north" then return room1() elseif move == "east" then return room4() else print("invalid move") return room3() end end function room4 () print("congratulations!") end 通过调用初始房间来开始这个游戏:
转自;programming in lua.
|
请发表评论