Lua笔记-6深入函数

来源:互联网 发布:php类的实例化关键字 编辑:程序博客网 时间:2024/06/18 05:09
--[[ 6 深入函数--      lua中,函数是一种第一类值(Fist-Class Value”,它们具有特定的词法域。--“第一类值是什么意思呢? 这表示在Lua中函数与其他传统类型的值(例如 数字和字符串)具有相同的权利。函数可以存储到变量中(无论全局变量--还是局部变量)或table中,可以作为实参传递其他函数,还可以作为其他函数的返回值。--      “词法域是什么意思? 这是指一个函数可以嵌套在另一个函数中,内部的函数可以访问外部函数中的变量。接下来就会看到,这项听似平凡的--特性将给语言带来极大的能力。---- ]]----[[-- Lua 中,函数与所有其他值一样都是匿名的,即它们都没有名称。当讨论一个函数名时(如:print),实际上是在讨论一个持有某个函数的变量。这--与其他变量持有各种值一个道理,可以以多种方式来操作这些变量。---- ]]----[[-- a={p=print}-- a.p("hello world")-- print=math.sin-- a.p(print(1))-- sin=a.p-- sin(10,20)-- ]]----[[-- 如果说函数是的话,那是否可以说函数就是由一些表达式创建的呢?是的,事实上在lua中最常见的是函数编写方式,如:--  function foo(x) return 2*x end-- 只是一种所谓的语法糖而已。也就是说,这只是以下代码的一种简化书写形式:-- foo = function(x) return 2*x end---- 因此,一个函数定义实际就是一条语句(更准确地说是一条赋值语句),这条语句创建了一种类型为函数的值,并将这个值赋予一个变量。可以将表--达式 “function(x) <body> end” 视为一种函数的构造式,就像table的构造式{}一样。将这种函数构造式的结果称为一个匿名函数---- ]]----[[ 由于函数在lua中是一种第一类值,所以不仅可以将其存储在全局变量中,还可以存储在局部变量甚至table的字段中,接下来还将看到,将函数--存储在table的字段中可以支持许多Lua的高级应用,例如 模块和面向对象编程。------ ]]----[[6.1 closure (闭合函数)--  若将一个函数写在另一个函数之内,那么这个位于内部的函数便可以访问外部函数中的局部变量,这项特征称之为词法域。虽然这种可见性准则--听上去很容易理解,但在实际应用中可能还会存在诸多问题。然而在编程语言中,词法域与第一类值函数是两项极其有用的概念,但支持它们的语言--却不多。---- ]]----[[   network={        {name="grauna",IP="210.26.30.34"},        {name="arraial",IP="210.26.30.23"},        {name="lua",IP="210.26.30.12"},        {name="derain",IP="210.26.23.20"},}--table.sort 它接受一个table并对其中的元素排序。有一个可选参数,另一个可以有的参数是一个次序函数,这个函数接受两个元素,并返回在-- 有序情况下第一个元素是否应该排在第二个元素之前。return语句返回truefalse来判断元素的位置顺序。table.sort(network,function (a,b) return (a.name<b.name) end )for k,v in pairs(network) do        print(k,v.name,v.IP)end--table.sort(network)--for k,v in pairs(network) do--      print(k,v.name,v.IP)--end--names={"Peter","Paul","Mary"}grades={Mary=10,Paul=7,Peter=8}--方式一:以匿名函数的方式来实现--table.sort(names,function(n1,n2) return grades[n1]>grades[n2] end)--for k,v in pairs(names) do--      print(k,v)--end--方式二:定义一个函数来实现,别忘记调用函数function sortbygrade(names,grades)        table.sort(names,function(n1,n2) return grades[n1]>grades[n2] end)end--在这个函数定义中,匿名函数中可以访问gradesgrades是外部函数sortbygrade的局部变量,在这个匿名函数内部,grades既不是全局变量也不是--局部变量,将其称为一个非局部的变量upvalue)。print('----------------------')sortbygrade(names,grades)for k,v in pairs(names) do        print(k,v)end--这里有个重要的概念,closure 是一个函数加上该函数所访问的所有"非局部变量(upvalue" .closure 即是为什么在Lua中允许这种访问呢? 原因在于函数是 "第一类值" newCounter = function()        local i=0        return function()                i=i+1                return i        endendc1=newCounter() --newCounter是一个函数的变量名,结果返回的是一个函数,这个函数的结果又返回的是一个整数i的值,--c1实际上匿名函数的变量名,c1() 实际上调用的是匿名函数,也就是一个closureprint(c1())print(c1())print(c1())print('----------------------')c2=newCounter()print(c2())print(c2())print(c2())----在这段代码中,匿名函数访问了一个"非局部变量" i,该变量用于保持一个计数器。初看上去,由于创建变量i的函数(newCount)已经返回,所以之后--每次调用匿名函数时,i都应是超出了作用范围的。但其实不然,Lua会以closure的概念来正确地处理这种情况。简单地讲,一个closure就是一个函数--加上该函数所需访问的所有"非局部的变量"。如果再次调用newCounter,那么会创建一个新的局部变量i,从而也将得到一个新的closure------ ]]----[[-- 非全局变量的函数(non-globalfunction--由于函数是一种"第一类值",因此一个显而易见的推论就是,函数不仅可以存储在全局变量中,还可以存储在table的字段中和局部变量中。-- 实例:io.read,math.sin--要在lua中创建这种函数,只需要将常规的函数语法与table语法结合起来使用即可:--lib={}--foo = function(x,y) return x+y end--goo = function(x,y) return x-y end--使用构造式:--lib={  foo = function(x,y) return x+y end  goo = function(x,y) return x-y end  }--或者使用lua的语法糖:--lib={}--function lib.foo(x,y) return x+y end--function lib.goo(x,y) return x-y end---- ]]----[[ 只要将一个函数存储到一个局部变量中,即可得到一个局部函数(local function,也就是说该函数只能在某个特定的作用域中使用。---- ]]----[[ 6.3 正确的尾调用-- lua中的函数还有一个有趣的特征,那就是Lua支持尾调用消除。所谓尾调用就是一种类似于goto的函数调用。当一个函数调用的是另一个函数的--最后一个动作时,该调用才算是一条尾调用--例如:--function f(x) return g(x) end--也就是说,当f调用完g之后就再无其他事情可做了。因此在这种情况下,程序就不需要返回那个尾调用所在的函数了,也就是说,不需要f函数栈里的-- 信息了。也就是说程序也不需要保存任何关于该函数的栈(stack)信息了。当g返回时,执行控制权可以跳过f函数,直接返回给那个调用点。有一些-- 语言实现可以得益于这个特点,使得在进行尾调用时不消费任何栈空间。将这种实现称为支持尾调用消除---- ]]----[[ 实例:-- function foo(n)--      if n>0 then return foo(n-1) end-- end----由于尾调用不会消费栈空间,所以一个程序可以拥有无数嵌套的尾调用。以上函数传入任何参数,不会造成栈溢出。尾调用是从一个函数跳到另--一个函数,而不是传统的函数调用!所有不需要保存栈空间内容!---- ]]----[[ 实例小房间游戏:--      |-----------|--      |room1|room2|--      |-----------|--      |room3|room4|--      |-----------|--function room4()   print("congratulations!")endfunction room1()   local move = io.read()   if move == "south" then      print("In the room3!")      return room3()      elseif move == "east" then         print("In the room2!")         return room2()      else         print("In the room1!")         print("Invalid move")         return room1()   endendfunction room2()   local move = io.read()   if move == "south" then return room4()      elseif move == "west" then return room1()      else         print("In the room2!")         print("Invalid move")         return room2()   endendfunction room3()   local move = io.read()   if move == "north" then return roo1()      elseif move == "east" then return room4()      else         print("Invalid move")         return room3()   endendr1=room1()----若没有"尾调用消除" 的话,每次用户的移动都会创建一个新的栈层,移动若干步之后就有可能会导致栈溢出。而尾调用消除则对用户移动的次数没有--任何限制。这是因为每次移动实际上都只是完成一条goto语句到另一个函数,而非传统的函数调用。---------- ]]--
0 0
原创粉丝点击