快速掌握Lua 5.3 —— 函数
2016-01-16 00:57
465 查看
Q:Lua中如何定义以及调用函数?
A:[code]function foo(arg1, arg2, ...) dosomething return ret1, ret2, ... or nothing end -- add all elements of array 'a'. function add (a) local sum = 0 for i,v in ipairs(a) do sum = sum + v end return sum end -- call it. a = {1, 2, 3, 4} foo(a)
Q:如何以面向对象的方式调用函数?
A:o:foo(x),其等同于
o.foo(o, x)。
Q:Lua编写的函数如何返回多个返回值?
A:在return的后面写多少个值,就可以让函数返回多少个值。[code]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
Q:Lua如何控制函数的返回值?
A:1、当你以一条语句的形式调用函数时,lua忽略函数所有的返回值(因为也没有变量接收)。
[code]foo()
2、当函数作为一系列表达式中最后一个表达式被调用时,lua保留函数所有的返回值。这一系列表达式包括:一个表达式对多个变量赋值,函数的参数传递,表的构造以及作为另一个函数的返回值。
[code]function foo() return 'a','b' end -- returns no results -- 一个表达式对多个变量赋值 x, y = foo() --> x = 'a', y = 'b' x, y, z = foo() --> x = 'a', y = 'b', z = nil x, y, z = 10, foo() --> x = 10, y = 'a', z = 'b' x, y, z = foo(), 10 --> x = 'a', y = 10, z = nil -- foo()不是表达式的最后,所以只有第一个返回值被保存。 -- 函数的参数传递 print(foo()) --> a b print(1, foo()) --> 1 a b print(foo(), 1) --> a 1 -- 注意!这里有个特殊情况: print(foo() .. "x") --> ax print("x" .. foo()) --> xa -- 注意这里也只保留了第一个返回值。 -- 推测:".."右边只接收一个参数,foo()所提供的多余的参数被丢弃了。 -- 表的构造 a = {1, foo()} --> a[1] = 1, a[2] = 'a', a[3] = 'b' a = {foo(), 1} --> a[1] = 'a', a[2] = 1 -- 作为另一个函数的返回值 function foo1() return 5, foo() end function foo2() return foo(), 5 end print(foo1()) --> 5 a b print(foo2()) --> a 5
3、其他情况下,lua只会保留函数的第一个返回值(在上面已经看到了一些情况)。
Q:如何强制限定函数只返回第一个返回值?
A:使用”()”将函数调用括起来,[code]function foo() return 'a','b' end print((foo())) --> a
Q:如何定义以及使用可变参数函数?
A:使用...,
[code]-- 用lua自己实现C语言中的printf()。 function printf(fmt, ...) --[[ select (index, ···) 如果index是一个数字,那么返回可变参数中第index个参数之后的所有参数值。 负的index代表从可变参数中最后一个参数开始计算index(-1是最后一个参数)。 否则,如果index只能是"#",select()会返回可变参数的总个数。]] for i = 1, select('#', ...) do -- select()返回可变参数的总个数。 -- 这里select()返回了第i个参数之后所有的参数值,但多余的值被抛弃了。 arg[i] = select(i, ...) end -- string.format()格式化字符串;io.write()向标准输出写。 return io.write(string.format(fmt, table.unpack(arg))) end
Q:Lua中的函数是一种”first-class values”,什么是”first-class values”?
A:与传统的变量(比如数字和字符串)拥有相同的权限。可以被存储在变量中,可以被存储在表中,可以作为参数传递,可以作为函数的返回值(C语言中的”函数指针”就有这些特性)。在Lua中函数被看作一种值,Lua中所说的函数名实际上是存储函数的变量的名字。Q:Lua中的函数具有”lexical scoping”特性,什么是”lexical scoping”?
A:函数可以访问包裹它的函数的值,[code]function foo() a=1 foo1() -- foo1()中可以访问a。 end
Q:什么是”Proper Tail Calls”?
A:”Proper Tail Calls”是一种特性,实现方式类似于C语言中”goto”调用。当一个函数的最后一个动作是调用另一个函数时,被调用的函数就具有”Proper Tail Calls”特性。[code]--[[ g()就是f()的"Proper Tail Calls"。 当在f()中调用完g()后,没有必要再返回到f()中,因为f()没有任何事要做了。 函数调用就是入栈出栈的过程,支持这种特性可以在递归函数的调用中节省大量的栈空间。]] function f(x) return g(x) end
Q:”Proper Tail Calls”的实际应用?
A:一个解谜小游戏,[code]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 -- 可以从room1开始游戏。 room1()
south
west
invalid move
east
congratulations!
Q:什么是”Closures”?
A:一个匿名函数,他能够访问包含他的”chunk”中的局部变量。”Closures”用到了”lexical scoping”以及”Proper Tail Calls”特性。[code]function newCounter () local i = 0 -- 只有返回匿名函数才会有这种特性,如果返回一个已定义的函数,不会有这种特性。 return function () i = i + 1 return i end end c1 = newCounter() -- c1 is a "closure". print(c1()) --> 1 print(c1()) --> 2 c2 = newCounter() -- c2 is another "closure". print(c2()) --> 1 print(c1()) --> 3 print(c2()) --> 2
Q:什么是”factory”?
A:创建”Closures”的函数。上面的newCounter()就是一个”factory”。
Q:什么是”sandbox”?
A:利用”Closures”所创建的安全的lua运行环境。从Internet上获取的lua程序在读懂它的源码前,我们不清楚它的安全性,不清楚它是否在其内部有一些危险的实现(比如读取隐私文件,删除系统文件等)。”Closures”的优势就在于它可以允许我们访问包含匿名函数的”chunk”中的局部变量,所以我们可以利用这一特性对一些敏感的函数做一些限制,从而创建一个安全的”sandbox”。[code]do local oldOpen = io.open io.open = function (filename, mode) --[[ access_OK()中可以列出允许访问的文件名,以及这些文件所允许的访问方式。 只有access_OK()返回"true"财允许调用原先的io.open()打开文件。]] if access_OK(filename, mode) then -- 可以访问包含匿名函数的"do-end"中的局部变量oldOpen。 return oldOpen(filename, mode) else return nil, "access denied" end end end -- [[这样做的强大之处在于,将原先的io.open()封装在内部, 外部程序调用的是我们创建的"sandbox"中安全的io.open(), 而外部程序想要访问原先的io.open(),也只能通过调用我们规定的io.open(),别无它法。]]
Q:如何将函数存储在table中?
A:[code]-- 使用table构造的形式。 Lib = { foo = function (x,y) return x + y end, goo = function (x,y) return x - y end } -- 使用赋值table中元素的形式。 Lib = {} Lib.foo = function (x,y) return x + y end Lib.goo = function (x,y) return x - y end -- 使用创建函数的方式。 Lib = {} function Lib.foo (x,y) return x + y end function Lib.goo (x,y) return x - y end
Q:如何创建本地函数?
A:使用local关键字。
[code]local function f(arg1, arg2, ...) dosomething return ret1, ret2, ... or nothing end
Q:如何定义一个本地的递归函数?
A:[code]-- 直接递归函数。 -- 方式1 local fact fact = function (n) if n == 0 then return 1 else return n*fact(n-1) end end -- 方式2 local function fact (n) if n == 0 then return 1 else return n*fact(n-1) end end -- 间接递归函数。 local f, g -- 定义间接递归函数时,必须先声明本地变量。 function g () ... f() ... end function f () ... g() ... end
附加:
1、lua中的函数如果只有一个参数,并且这个参数是字符串常量或者是一个表的构造,则函数调用时括号可以不写:[code]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({})
建议写上比较好,又规范,又不容易混乱。
2、调用函数时,如果实参的数量多于形参的数量,则多余的实参会被忽略;而如果实参的数量少于形参的数量,剩余的形参值为nil。
[code]function foo(a, b) print(a, b) end foo(5, 6, 7) --> 5 6 foo(5) --> 5 nil
3、注意,当函数return一个表达式时,并不需要像C语言那样为了规范而加上一个括号,
[code]function foo() return 'a','b' end -- 这样些只能得到foo()的第一个返回值,这也许是你想要的,也许不是。 function foo1() return (foo()) end
4、通过table实现可选参数函数。
[code]--[[ 对于参数很多的函数,有时很难记住参数的名字和参数的顺序以及哪些参数是可选的。 通过table让你在调用这类函数时可以随意指定参数的顺序,并且可以只传递需要设定的参数。]] function create_window(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 _create_window(options.title, options.x or 0, -- default value is 0 options.y or 0, -- default value is 0 options.width, options.height, options.background or "white", -- default is "white" options.border -- default is false (nil) ) end
5、既然function被看作一种可以被变量存储的值,那么也就可以按照变量赋值的操作来创建以及使用函数:
[code]foo = function (x) return 2*x end print(foo(5)) --> 10
6、一些被误认为是”Proper Tail Calls”的情况:
[code]-- 在调用g()之后,还需要丢弃g()的返回才能return。 function f() g() return end -- 在调用g()之后,还需要做加法。 function f() return g() + 1 end -- 在调用g()之后,还需要做"or"操作。 function f() return x or g() end -- 在调用g()之后,还需要截取g()的第一个返回值。 function f() return (g()) end
7、”Closures”的一个应用,将math.sin()转换为接收角度值,
[code]do local oldSin = math.sin -- 原先的math.sin()接收弧度值。 local k = math.pi/180 -- 弧度转角度系数。 math.sin = function (x) -- 新的math.sin()接收角度值。 return oldSin(x*k) -- 弧度转为角度调用原先的math.sin()。 end end
8、函数存储在表中方便管理。比如可以将函数以及其所需数据都存储在一个table中,他们形成一个整体来管理。
9、以下定义本地的递归函数的方式是错误的,
[code]local fact = function (n) if n == 0 then return 1 else return n*fact(n-1) -- buggy end end
当Lua编译”n*fact(n-1)”时,”local fact”还并不存在。因此这样写的结果是使用了全局的”fact”,而非那个”local fact”。为了解决这个问题,需要”Q & A”中提到的正确的方式那样,先定义”local fact”。
相关文章推荐
- Lua中的table函数库
- lua和c的亲密接触
- sicily 递归练习 1005. Arithmetic Expression Evaluation
- lua和c的亲密接触
- lua BaseClass
- lua 怎么写两个字符串相加
- (java)Evaluate Reverse Polish Notation
- LUA解析json小demo
- LUA解析json小demo
- windows下编译LUA-cjson
- windows下编译LUA-cjson
- lua c closure的使用
- lua 绑定c++
- liunx 下 C/C++调用luajit
- 用lua写Android界面学习笔记
- [转载]轻量级Lua IDE ZeroBrane Studio 的使用技巧和汉化
- 从零开始——Windows环境lua编程
- leetcode--Evaluate Reverse Polish Notation
- Lua 表达式
- Lua中and、or的一些特殊用法