《lua程序设计》读书笔记 第六章:深入函数
来源:互联网 发布:ftp客户端软件 编辑:程序博客网 时间:2024/05/19 22:45
在Lua中,函数是一种“第一类值”,它们具有特定的词法域。“词法域”是什么意思呢?这是指一个函数可以嵌套在另一个函数中,内部的函数可以访问外部函数中的变量。
在Lua中有一个容易混淆的概念是,函数与所有其他值一样是匿名的,即它们都没有名称。当讨论一个函数名时,实际上是在讨论一个持有某函数的变量。函数定义实际上是一个表达式:
function foo(x) return 2*x end其实际是foo = function(x) return 2*x end, 其实其是一条赋值语句。
可以将表达式“function(x) < body > end”视为一个函数的构造式,将构造式的结果称为一个匿名函数。
一个函数可以作为另一个函数的参数,接受另一个函数作为实参的函数,称为高阶函数。
network = {{name = "grauna", IP="210.26.30.34"} {name = "lua", IP = "210.26.23.12"}} table.sort(network, function(a, b) return (a.name > b.name) end) -->传入一个匿名函数为参数
6.1 closure(闭包函数)
若将一个函数写在另一个函数内,那么这个位于内部的函数可以访问外部函数中的局部变量,这项特征被称为“词法域”。为什么在Lua中允许这种访问呢?原因在于函数是“第一类值”。看如下代码:
function newCounter() local i = 0 return function() i = i + 1 return i endendlocal c1= newCounter()c1() -->1c1() -->2
简单来讲,一个closure就是一个函数加上这个函数访问的所有“非局部变量”。如果再次调用newCounter,那么它会创建一个新的closure。
local c2 = newCounter()print(c2()) -->1print(c1()) -->3
从技术上讲,Lua中只有closure而没有函数,函数只是一种特殊的closure。
closure还有一些常用的用法,比如重定义一个预设的函数:
oldSin = math.sinmath.sin = function(x) return oldSin(x*math.pi . 180)end
可以使用同样的技术创建一个安全的运行环境,即所谓的“沙盒”。
do local oldOpen = io.open local access_OK = function(filename, mode) < 检查一些访问权限 > end io.open = function(filename, mode) if access_Ok(filename, mode) then return oldOpen(filename, mode) else return nil, "access denied" end endend
6.2 非全局的函数
在Lua中,函数通常存储在一个table中,而不是作为全局变量使用:
Lib1 = {} Lib1.foo = function(x, y) return x + y end Lib2 = {foo = function(x,y) return x + y end}
除此之外,Lua还提供了另一种语法来定义这类函数:
Lib = {} function Lib.foo(x,y) return x+y end
对于这种局部函数的定义,Lua还支持一种特殊的“语法糖”
local function foo(< 参数 >) <函数体>end
另外在定义递归的局部函数有一点要特别注意。看以下代码:
local fact = function(n) if n == 0 then return 1 else return n*fact(n-1) endend
这样定义的错误的,关键在于其先计算右边的表达式,然后再定义左边变量,而在调用fact(n-1)时,fact变量尚未定义完毕,因此其实际会调用全局的fact,而非其本身。
正确的写法如下,应先定义局部变量fact:
local factfact = function(n) if n==0 then return 1 else return n * fact(n -1) endend
而对于local function foo<参数> <函数体> end的定义方式,Lua将其展开为
local foofoo = function(<参数>) <函数体> end
所以使用这种方式定义递归函数不会有错误。
当然,这个技巧对于间接递归的函数是无效的,你必须使用一个明确的前置声明。
local f,g function g() f() end function f() -->注意不要用local function,否则其会创建一个f覆盖前面的变量 g() end
6.3正确的尾调用
Lua支持“尾调用消除”。所谓“尾调用”就是一种类似于goto的函数调用。当一个函数调用时另一个函数的最后一个动作时,该调用才算是一条“尾调用”。
function f(x) return g(x) end
在上述调用中,当f调用完g后便无事可做了,因此程序不在需要保留函数f的堆栈信息,所以在进行尾调用时不耗费任何栈空间,这种实现便是“尾调用消除”。由于“尾调用”不会消耗栈空间,所以一个程序可以拥有无数嵌套的“尾调用”:
function foo(n) if n > 0 then return foo(n-1) endend
在Lua中“尾调用”的一大应用就是编写“状态机”。这种程序以一个函数来表示一个状态。
- 《lua程序设计》读书笔记 第六章:深入函数
- Lua程序设计第二版(笔记) 第六章深入函数
- Lua程序设计(第2版)第六章学习随笔——深入函数
- Lua学习笔记 第六章 深入函数
- 《lua程序设计》读书笔记 第五章:函数
- Lua程序设计:第6章 深入函数
- Windows程序设计读书笔记:第一章----第六章
- 《lua程序设计》读书笔记 第三章:表达式
- 《lua程序设计》读书笔记 第四章:语句
- <Lua程序设计>读书笔记
- 读书笔记:深入理解计算机系统 第六章
- 《lua程序设计》读书笔记 第一章:开始
- 读书笔记_GDI+程序设计_第六章(GraphicsPaths和Regions)
- JavaScript高级程序设计(第3版)第六章读书笔记
- JavaScript高级程序设计读书笔记(第六章)(一)
- JavaScript高级程序设计读书笔记(第六章)(二)
- JavaScript高级程序设计读书笔记(第六章)(三)
- JavaScript高级程序设计读书笔记(第六章)(四)
- DataTables解决 Cannot reinitialise DataTable 问题
- 【life】:亏欠感与独立感
- [Windows驱动开发](一)序言
- Python--小笔记
- fanil修饰属性
- 《lua程序设计》读书笔记 第六章:深入函数
- [Windows驱动开发](二)基础知识——数据结构
- post方式提交序列化对象
- nodejs事件循环学习笔记
- 常用api网址
- yarn的cpu资源隔离
- [Windows驱动开发](三)基础知识——驱动例程
- Nan空值
- 品质化时代 CP如何把控手游产品质量关