第六章: 再论函数
Lua 中的函数是带有词法定界(lexical scoping)的第一类值(first-class values)。
第一类值指:在 Lua 中函数和其他值(数值、字符串)一样,函数可以被存放在变
量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值。
词法定界指:被嵌套的函数可以访问他外部函数中的变量。这一特性给 Lua 提供了
强大的编程能力。
先给出一些实例,看看lua的函数是如何跟普通变量一样可以进行赋值 作为参数。。。
function foo(x) return x * 2 end print(foo(3)) -- 6 a = {p = print} a.p("hello world") -- hello world print = math.sin -- print new refer to the sin function a.p(print(1)) sin = a.p sin(10,20) -- 10 20 funciton print network = { {name = "grauna", IP = "210.26.30.34"}, {name = "arraial", IP = "210.26.30.23"}, {name = "lua", IP = "210.26.23.12"}, {name = "derain", IP = "210.26.23.20"}, } table.sort( network, function (a,b) return (a.name > b.name) end) --没有名字的函数 匿名函数
当一个函数内部嵌套另一个函数时,内部的函数体可以访问外部的函数的局部变量,这种特征叫做词法界定。
names = {"Peter","Paul","Mary"} grades={Mary=10,Paul=7,Mary=5} table.sort(names,function (n1,n2) return grades[n1] > grades[n2] end) --实现上述功能函数 function sortBygrade(names,grades) table.sort(names,function (n1,n2) return grades[n1] > grades[n2] -- compare the grades end) end
一个计数器的例子:
function newCounter() local i = 0 return function () i = i + 1 return i end end c1 = newCounter() print(c1()) --1 print(c1()) --2 c2 = newCounter() print(c2()) --1 print(c1()) –3
技术上来讲, 闭包指值而不是指函数,函数仅仅是闭包一个原型声明;尽管如此
也可以使用闭包来实现函数重定义:
print(math.sin(100)) oldSin = math.sin math.sin = function (x) return oldSin(x * math.pi / 180) end print(math.sin(100))
2、非全局函数
Lua的函数可以作为全局变量也可以作为局部变量,函数使用table作用域, 有三种定义方式
-- 表和函数放在一起 Lib = {} Lib.foo = function (x,y) return x + y end Lib.goo = function(x,y) return x - y end -- 使用表构造函数 Lib2 = { foo = function(x,y) return x + y end, goo = function(x,y) return x - y end } --定义方式三 function Lib.square(x) return 2 * x end function Lib.sqrt(x) return math.sqrt(x) end print(Lib.foo(2,3)) print(Lib2.goo(4,5)) print(Lib.square(2)) print(Lib.sqrt(3))
3、正确的尾调用
Lua函数的另外一个有趣的特征是可以正确的处理尾调用(proper tail recursion)
尾调用是一种类似在函数结尾的goto调用,当函数最后一个动作是调用另外一个函数时,我们称这种调用为尾调用。
例如:
function f(x) return g(x) end
最后一个尾调用的例子:
尾调用可以理解为 goto:
这个迷宫游戏是典型的状态机,每个当前的房间是一个状态。我们可以对每个房间
写一个函数实现这个迷宫游戏,我们使用尾调用从一个房间移动到另外一个房间。一个
四个房间的迷宫代码如下:
function room1() local move = io.read() if move == "south" then return room3() elseif move =="east" then return room2() else print("invalid move") return room1() 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("congratilations!") end room1()