Lua函数以及闭合函数的理解

来源 http://blog.csdn.net/mydad353193052/article/details/48731467

词法域和第一类型

在C/C++,C#或者Java等传统语言中,一个函数A,如果想调用另一个函数B,并且B需要访问A中的变量,那么A就需要向B传递参数,参数形式可以是普通类型,指针,或者引用。(C#中有out输出参数和ref引用,专门的关键字来做这件事)但是在Lua中,呵呵,不必如此。

Lua中有一个“词法域”的概念。即B可以访问他所需要访问的所有“非局部变量”(后面会解释为什么叫双引号下的非局部变量)。但是这一切的说说笑笑都必须要有前提——在Lua中,函数是“第一类型”。就是说函数和int,string,float等泛泛之辈一样,都TM是类型,都是菜。而函数名则可以理解为,拿着函数实体的变量。请看下面这个例子:

  1. a = {p = print}
  2. a.p("hello bitch")              ---hello bitch
  3. print = math.sin            ---'print'现在是正弦函数
  4. a.p(print(1))               ---0.8414709848079
  5. sin = a.p               ---'sin'现在是print函数
  6. sin(10 .. 20)               ---1020

正所谓似鸡非鸡,似鸭非鸭。鸡有可能变鸭,鸭也可以手术变鸡。

那么说函数是变量,他还就蹬鼻子上脸,给变量一会看看——Lua常见的函数编写方式:

function foo(x)  return  2*x  end

其实他是一下这句话的简化形式:

foo = function (x)  return  2*x  end

所以,一个函数的定义实际就是一条赋值语句,这条语句首先创建一个函数类型的值,然后将这个值赋予这个函数变量。也可以将表达式    function(x)<body>end  理解为一种函数的构造式。就像table的构造式 {} 一样。

将这种函数构造式的结果称为一个“匿名函数”。下面这个例子就是使用匿名函数来做参数。

table.sort,是对一个table表中的内容按照一定规则排序。但是sort并没有提供具体的规则,规则的指定完全交给使用者自己定义。升序,降序,按key顺序等等。sort接收两个元素,第一个元素是table,第二个元素就是排序规则。那我们在这里就用匿名函数来做这个sort的第二个元素——排序规则。

  1. network = {
  2. {name = "grauna", NICK = "lululala"},
  3. {name = "araadd", NICK = "bababu" },
  4. {name = "oo", NICK = "ppreea"},
  5. {name = "zzdafae", NICK = "ddadf"},
  6. }
  7. for i=1, #network do
  8. print(network[i].name)
  9. end
  10. table.sort(
  11. network,
  12. function(a,b)
  13. return (a.name > b.name)
  14. end
  15. )
  16. for k,v in pairs(network) do
  17. print(k,v.name)
  18. end

二、闭合函数

在理解了上面这些思想之后,我们来想想closure。还是刚才说的, A==>>B,A是B的外部函数,B可以访问A中的局部变量。直接上菜说:

  1. names = {"Peter", "Paul", "Mary"}
  2. grades = {Mary = 10, Paul = 7, Peter = 8}
  3. table.sort(
  4. names,
  5. function(n1, n2)
  6. return grades[n1] > grades[n2]
  7. end
  8. )
  9. for k,v in ipairs(names) do
  10. print(k,v)
  11. end

用一个单独的函数来调用:

  1. names = {"Peter", "Paul", "Mary"}
  2. grades = {Mary = 10, Paul = 7, Peter = 8}
  3. function sortbygrade(cnames, cgrades)
  4. table.sort(
  5. cnames,
  6. function(n1, n2)
  7. return cgrades[n1] > cgrades[n2]
  8. end
  9. )
  10. return cnames
  11. end
  12. for k,v in ipairs(sortbygrade(names, grades)) do
  13. print(k,v)
  14. end

输出结果同上。

可见,传递给sort的匿名函数可以访问参数cgrades,而cgrades是外部函数sortbygrade的局部变量。而在这个匿名函数内部,cgrades既不是全局变量也不是局部变量,将其称为一个“非局部的变量”。

再看下面这个例子:

  1. function newCounter()
  2. local i =  0
  3. return function()
  4. i = i + 1
  5. return i
  6. end
  7. end
  8. c1 = newCounter()
  9. print(c1())         ---1
  10. print(c1())         ---2
  11. c2 = newCounter()
  12. print(c2())         ---1
  13. print(c1())         ---3
  14. print(c2())         ---2

初次见到这个输出结果,有些摸不到后脑勺。为啥第二次print(c1())结果是2呢?

仔细一想,这还得说那句老话,Lua中函数是第一类型。(别骂娘)

你想想,在C++中,如果你定义一个int a=1;那么编译器只会在栈中开辟一块空间来存a,直至释放。

那么c1也是这回事儿,你第二次print,实际上还是在  第一次在栈中开辟的那块空间里  折腾。所以此时栈中的local i已经不是当年那个 = 0 的小姑娘了,人家已经长成 1 了。所以你再print,就在1的基础上变成2了。

而c2跟c1不一样,c2是隔壁家的姑娘,又是一块栈空间,所以c2的第一次还是1。 以此类推,后面的print结果就顺理成章了。

Lua函数以及闭合函数的理解的更多相关文章

  1. lua闭合函数

    function count( ... ) return function( ... ) i = i+ return i end end local func = count(...) print(f ...

  2. [Lua] 迭代器 闭合函数 与 泛型for

    首先看看一下闭合函数(closure),见如下代码: function newCounter() local i = 0 -- 非局部变量(non-local variable) return fun ...

  3. Lua中的closure(闭合函数)

    词法域:若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,这项特征称之为“词法域”. 例:假设有一个学生姓名的列表和一个对应于没个姓名的年级列表,需要根据每个学生的 ...

  4. Lua中调用C函数

    Lua利用一个虚拟的堆栈来给C传递值或从C获取值.每当Lua调用C函数,都会获得一个新的堆栈,该堆栈初始包含所有的调用C函数所需要的参数值(Lua传给C函数的调用实参),并且C函数执行完毕后,会把返回 ...

  5. lua学习之深入函数第一篇

    深入函数第一篇 函数是第一类值,具有特定的词法域 第一类值 第一类值的意思是函数与 lua 中的其他类型如数字,字符串具有相同的权力 函数可以存储到全局变量或局部变量变量,还可以存储到 table 中 ...

  6. 深入理解javascript函数定义与函数作用域

    最近在学习javascript的函数,函数是javascript的一等对象,想要学好javascript,就必须深刻理解函数.本人把思路整理成文章,一是为了加深自己函数的理解,二是给读者提供学习的途径 ...

  7. lua如何调用C++函数

    第一步是定义函数.所有在Lua中被调用的C/C++函数将使用下面一类指针进行调用: typedef int (*lua_CFunction) (lua_State *L); 换句话说,函数必须要以Lu ...

  8. Lua中的常用函数库汇总

    lua库函数 这些函数都是Lua编程语言的一部分, 点击这里了解更多. assert(value) - 检查一个值是否为非nil, 若不是则(如果在wow.exe打开调试命令)显示对话框以及输出错误调 ...

  9. lua堆栈操作常用函数学习二

    /* ** basic stack manipulation */ LUA_API int  <strong> (lua_gettop) (lua_State *L);  </str ...

随机推荐

  1. 微软云Linux服务器 Mysql、tomcat远程连接错误解决办法

    在微软云linux服务器成功配置好mysql.tomcat,通过外部链接却发现一直错误.Mysql 一直提示错误代码2003, tomcat连接一直提示EOF. 反复检查配置都无问题,最后得知是微软云 ...

  2. ArrayList,LinkedListd等容器使用时注意点:

    1.对这两个List(包括其他的类似容器),如果向里面加入一个元素(引用数据类型),那么这个List里面保存的是这个对象的引用: 如果想要避免这种现象可以这样:在加入新的元素时不直接压,将已有的对象复 ...

  3. 修改yum源

    安装 centos 之后,修改 yum 源到其它国内源 1. 备份原文件 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Ba ...

  4. IS 和AS

    http://www.cnblogs.com/haiyang1985/archive/2009/03/12/1410023.html 1一. as 运算符用于在兼容的引用类型之间执行某些类型的转换. ...

  5. SSM之整合Redis

    Redis安装与使用 第一步当然是安装Redis,这里以Windows上的安装为例. 首先下载Redis,可以选择msi或zip包安装方式 zip方式需打开cmd窗口,在解压后的目录下运行redis- ...

  6. 程序编译没错,运行报错:无法定位程序输入点GT_BufLaserFollowRatio(这是函数)于动态链接库GTS.DLL上

    :DLL里面没有导出该函数    :DLL没放进DEBUGS文件夹 (当时的情况是这个)

  7. Hadoop 一: NCDC 数据准备

    Hadoop 本文介绍Hadoop- The Definitive Guide一书中的NCDC数据准备,为后面的学习构建大数据环境; 环境 3节点 Hadoop 2.7.3 集群; java vers ...

  8. 微信支付之h5方式(非微信内置浏览器中支付)

    这两天完成了公司网站手机和PC端的支付对接,就是支付宝和微信. 对接完后有所感触,我们来聊一聊,微信支付的坑,为什么这么说呢,因为我在对接完支付宝后是很愉快的,基本上在demo上稍加修改就ok了, 对 ...

  9. 接口测试——HttpClient工具的https请求、代理设置、请求头设置、获取状态码和响应头

    目录 https请求 代理设置 请求头设置 获取状态码 接收响应头 https请求 https协议(Secure Hypertext Transfer Protocol) : 安全超文本传输协议, H ...

  10. TeamFlowy——结合Teambition与Workflowy提高生产力

    Teambition是一个跨平台的团队协作和项目管理工具,相当于国外的Trello.使用Teambition可以像使用白板与便签纸一样来管理项目进度,如下图所示. Teambition虽然便于管理项目 ...