lua function
lua八种数据类型
- nil 全局变量没被赋值默认为 nil,删除变量就赋值为 nil
- boolean(bool) false和nil为假,其它都为真
- number 实数,可以是整数,浮点数
- string 字符串,一旦赋值不能被修改,可以通过方法string.gsub()来修改
- function 函数
- table 数组、容器
- userdata (类,其它语言转换过来就变成userdata类型)
- thread 线程
lua function
- 在这几种数据类型中,其中nil,boolean,number比较简单,string需要注意一下它里面的几个内置函数,这个网上都有介绍的,这里主要先介绍一下function
- Lua支持面向对象,操作符为冒号‘:’ o:foo(x) <==> o.foo(o, x)
- Lua程序可以调用C语言或者Lua实现的函数
- 函数的参数跟局部变量一样,用传入的实参来初始化,多余的实参被丢弃,多余的形参初始化为nil
- 最近细看了一下,lua function有一些比较特别也比较有用的语法
- 多个返回值
- 变参
- 函数变量(函数名)
- closure(闭合函数)
- 非全局函数
- 尾调用
多个返回值
-
不同于常规函数,Lua的函数可以返回多个返回值
function foo() return "a","b" end print(foo()) a, b
- 运用:函数需要返回多个值的时候,没必要组合成table,可以按照返回值的重要程度,指定顺序返回。获取值的时候,可以选择性获取自己比较感兴趣位置的参数赋值即可,不用关心其他参数的值
-
如果一个函数调用不是一系列表达式的最后一个元素,那么将只产生一个值
x,y=foo(),"c" print(x,y) a,c
-
table构造式可以完整接受一个函数调用的所有结果
t={foo()} print(#t) 2
- return语句,诸如return f()这样的语句,将返回f的所有返回值,如果写成return (f(x)),将只返回一个值。
- 运用:需要转成table时也可以在外部得到值了以后,用table构造
-
unpack,他接受一个数组作为参数,并从下标1开始返回该数组的所有元素,unpack递归实现
function unpack (t, i) i = i or 1 if t[i] ~= nil then return t[i], unpack(t, i + 1) end end
运用:可以灵活实现table多参数的解包,对应压缩包函数pack
变参
-
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个点代表了实参。
- 表达式"…"的行为类似于一个具有多重返回值的函数,他返回的是当前函数的所有变长参数
function foo(a,b,c) //等价于 function foo(...) local a,b,c=... end
- 运用:这里我的感觉是写法简洁,传递的时候不会传错,即如果这个函数只是这个中间作用,并没有使用这个参数,这样用比较好,但是如果一般只有一次传值,且马上使用的话,还是尽量不要用变参
函数变量(函数名)
-
函数与所有其他值一样都是匿名的,即他们都没有名称。当讨论一个函数名时(例如print)。实际上是在讨论持有某函数的变量,这与其他变量持有各种值一个道理
a = { p = print } a.p("Hello World") b = print b("Hello World") function foo(x) return 2 * x end //等价于 foo = function(x) return 2 * x end
-
所以我们可以这样去理解,一个函数定义实际上就是一条语句(更准确说是赋值语句)。这条语句创建了一种类型为”函数“的值
closure(闭合函数)
-
若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量
-
一个closure就是一个函数加上该函数所需访问的所有“非局部变量”
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
-
其函数体内的局部变量i被称为"非局部变量",该变量被newCounter函数体内的匿名函数访问并操作。再有就是在函数newCounter返回后,其值仍然被保留并可用于下一次计算
扫描二维码关注公众号,回复: 12442943 查看本文章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每次在给新的闭包变量赋值时,都会让不同的闭包变量拥有独立的"非局部变量"
非全局函数
-
Lua中的函数不仅可以直接赋值给全局变量,同时也可以赋值给其他类型的变量,如局部变量和table中的字段等
Lib = {} Lib.add = function(x,y) return x + y end Lib.sub = function(x,y) return x - y end //或者是在table的构造式中直接初始化,如: Lib = { add = function(x,y) return x + y end, sub = function(x,y) return x - y end } //除此之外,Lua还提供另外一种语法来定义此类函数,如: Lib = {} function Lib.add(x,y) return x + y end function Lib.sub(x,y) return x - y end
-
对于这种局部函数,Lua还提供另外一种更为简洁的定义方式,如:
local function f(x,y) return x + y end //该写法等价于: local f f = function(x,y) return x + y end
-
**运用:**对于写递归函数,需要先申明这个变量,才能使用
//错误递归 local f = function() f() if true then return end end f() //应改成 local f f = function() f() if true then return end end f()
-
尾调用
-
在Lua中支持这样一种函数调用的优化,即“尾调用消除”。我们可以将这种函数调用方式视为goto语句,如:
function f(x) return g(x) end
-
由于g(x)函数是f(x)函数的最后一条语句,在函数g返回之后,f()函数将没有任何指令需要被执行,因此在函数g()返回时,可以直接返回到f()函数的调用点。由此可见,Lua解释器一旦发现g()函数是f()函数的尾调用,那么在调用g()时将不会产生因函数调用而引起的栈开销。这里需要强调的是,尾调用函数一定是其调用函数的最后一条语句,否则Lua不会进行优化。
-
一条“尾调用”就好比是一条“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
-
若没有”尾调用消除“的话,每次用户的移动都会创建一个新的stack,移动若干步之后就有可能导致栈溢出。
小结
- 其实function的这几种用法,我们平时也遇到过很多,需要我们合理的运用