1.函数是一种“第一类值”,他们具有特定的词法域。
2.第一类值,表示Lua中函数与其他类型的值(例如数字)具有相同的权利,函数可以存储到变量或table中,也可以作为实参,还可以作为其他函数的返回值。
3.词法域,是指一个函数可以嵌套在另一个函数中,内部函数可以访问外部函数的变量。
4.函数与其他值一样都是匿名的。即,一个函数名(如print)是一个持有某函数的变量,与其他变量持有各种值的道理是一样的。
函数是匿名的5.function foo(x) return 2*x end 只是所谓的“语法糖”,是 foo = function(x) return 2*x end 的简化书写形式。此时,表达式“funcrion(x) <body> end”相当于函数的构造式,结果为一个“匿名函数”。
匿名函数6.接受另一个函数作为实参的函数(例如sort),称为“高阶函数(higher-order function)”。高阶函数是一种强大的编程机制,应用匿名函数来创建高阶函数所需的实参则可以带来更大的灵活性。
闭合函数(closure)
假设有一个学生姓名的表和一个对应名称的年级表 根据学生的年级来对姓名排序1.上例中,传递给sort的匿名函数可以访问参数grades,而grades是外部函数sortbygrade的局部变量(参数也是一种局部变量)。在这个匿名函数内部,grades既不是全局变量也不是局部变量,将其称为一个“非局部的变量(non-local variable 或 upvalue)”。
2.一个闭合函数就是一个函数加上该函数所需访问的所有“非局部的变量”。从技术上来说,Lua中只有闭合函数,因为函数本身就是一种特殊的closure(即没有非局部变量的closure)。
3.closure结构通常涉及两个函数:closure本身和用于创建该closure的工厂函数。
4.closure对于回调函数很有用。
colsure在回调中的使用5.closure还可用于重新定义某些函数。
重新定义sin 将老版sin存到私有变量后,只有通过新版本的sin才能访问它6.利用closure创建沙盒(sandbox),即一个安全的运行环境。
限制程序访问文件(经过重新定义后,程序只能通过受限版本的os.open函数来调用原有的函数,通过将不安全的版本保存到closure的一个私有变量中,从而使得外部再也无法直接访问到原来的版本)非全局函数(non-global function)
1.只要将一个函数存储到一个局部变量中,即得到了一个“局部函数”,也就是说该函数只能在某个特定的作用域中使用。
词法域确保程序包中的其他函数可以使用局部函数2.对于局部函数的定义,Lua还支持一种特殊的“语法糖”:
局部函数定义 上述语法糖展开3.在定义递归的局部函数时,需要特别注意:
递归中的局部函数 上述递归函数的简化4.间接递归中,必须使用一个明确的前向声明(forward declaration):
间接递归正确的尾调用(proper tail call)
1.尾调用(tail call),一个函数调用是另一个函数的最后一个调用动作,该调用就称为“尾调用”。判断一个调用是不是尾调用的准则是“一个函数在调用完另一个函数之后,是否就无事可做”。即“return <func>(<args>)”这样的调用形式。
对g的调用就是尾调用 f调用完g之后还需丢弃g的返回值,所以不算尾调用 不是尾调用 在调用前会对参数求值,所以它们可以是任意复杂的表达式2.尾调用消除(tail-call elimination),尾调用后,函数就无事可做了,因此程序就不需要保存任何关于该函数的栈信息,执行控制权在g返回时,直接返回到f中调用的点上。这使得在进行尾调用时不耗费任何栈空间。
3.由于尾调用不耗费栈空间,所以一个程序可以拥有无数嵌套的尾调用:
n为任何数字时,都不会造成栈溢出4.尾调用的一大应用就是编写“状态机”。
5.举一个简单的迷宫游戏例子:一个迷宫有几间房间,每个房间中有东南西北4扇门,用户在每一步移动中都需输入一个移动的方向。如果某个方向上有门,那么用户可以进入对应的房间,不然,程序就打印一条警告。游戏的目标是让用户从最初的房间走到最终的房间。
当前房间就是一个状态,将每间房间都实现为一个函数,并使用尾调用来实现从一间房间移动到另一间房间。使用尾调用对用户的移动次数没有任何限制。
网友评论