Lua笔记-6深入函数

--[[ 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
        end
end

c1=newCounter() --newCounter是一个函数的变量名,结果返回的是一个函数,这个函数的结果又返回的是一个整数i的值,
--c1实际上匿名函数的变量名,c1() 实际上调用的是匿名函数,也就是一个closure
print(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!")
end

function 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()
   end
end

function 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()
   end
end


function room3()
   local move = io.read()
   if move == "north" then return roo1()
      elseif move == "east" then return room4()
      else
         print("Invalid move")
         return room3()
   end
end


r1=room1()
--
--若没有"尾调用消除" 的话,每次用户的移动都会创建一个新的栈层,移动若干步之后就有可能会导致栈溢出。而尾调用消除则对用户移动的次数没有
--任何限制。这是因为每次移动实际上都只是完成一条goto语句到另一个函数,而非传统的函数调用。
--
--
--
--
-- ]]--
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值