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

词法域和第一类型

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

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

[plain]  view plain  copy
  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的构造式 {} 一样。

扫描二维码关注公众号,回复: 2172397 查看本文章

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


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

[plain]  view plain  copy
  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.       
  11. table.sort(network, function(a,b) return (a.name > b.name) end)  
  12.   
  13. for k,v in pairs(network) do  
  14.     print(k,v.name)  
  15.     end  



二、闭合函数

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

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


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

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

输出结果同上。

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

再看下面这个例子:

[plain]  view plain  copy
  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结果就顺理成章了。


猜你喜欢

转载自blog.csdn.net/qq_39887964/article/details/80581132