当前位置:早雪网网络学院编程文档游戏开发 → Programming in Lua翻译--More about Functions

Programming in Lua翻译--More about Functions

减小字体 增大字体 作者:未知  来源:从互联网收集整理并转载  发布时间:2005-6-3 0:56:07
原文参考: http://www.lua.org/pil/index.html
翻译本文章是个人爱好Lua所至,转载请注明出处和作者.版权归原作者所有,未经允许不得将文章用于商业目的,否则造成的一切后果由该组织或个人承担,本人不承担任何法律及连带责任.请自觉遵守.
6.More about Functions
Lua中的函数是带有词法定界(lexical scoping)的第一类值(first-class values).
 第一类值指:在Lua中函数和其他值(数值,字符串)一样,函数可以被存放在变量中,也可以存放在表中,可以作为函数的参数,还可以作为函数的返回值.
 词法定界指:函数可以访问他内部嵌套的函数中的变量.这一特性给Lua提供了强大的编程能力.
 Lua中关于函数稍微难以理解的是函数也可以没有名字,匿名的.当我们提到函数名(比如print),实际上是说一个指向函数的变量,像其持有其他类型值的变量一样
  a = {p = print}
     a.p("Hello World") --> Hello World
     print = math.sin  -- `print' now refers to the sine function
     a.p(print(1))     --> 0.841470
     sin = a.p         -- `sin' now refers to the print function
     sin(10, 20)       --> 10      20
 既然函数是值,那么表达式也可以创建函数了,Lua中我们经常这样写:
  function foo (x) return 2*x end
 这实际上是利用Lua提供的"语法上的甜头"(syntactic sugar)的结果,下面是原本的函数:
  foo = function (x) return 2*x end
 函数定义实际上是一个赋值语句,将类型为function的变量赋给一个变量.我们使用function (x) ... end 来定义一个函数和使用{}创建一个表一样.
 table标准库提供一个排序函数,接受一个表作为输入参数并且排序表中的元素.这个函数必须能够对不同类型的值(字符串或者数值)按升序或者降序进行排序.Lua不是尽可能多地提供参数来满足这些情况的需要,而是接受一个排序函数作为参数(类似C++的函数对象),排序函数接受两个排序元素作为输入参数,并且返回两者的大小关系,例如:
  network = {
        {name = "grauna",  IP = "210.26.30.34"},
        {name = "arraial", IP = "210.26.30.23"},
        {name = "lua",     IP = "210.26.23.12"},
        {name = "derain",  IP = "210.26.23.20"},
      }
 如果我们想通过表的name域排序:
  table.sort(network, function (a,b)
        return (a.name > b.name)
     end)
 以其他函数作为参数的函数在Lua中被称作高级函数,高级函数在Lua中并没有特权,只是Lua把函数当作第一类函数处理的一个简单的结果.
下面给出一个绘图函数的例子:
  function eraseTerminal ()                                   
              io.write("\27[2J")                                    
            end                                                     
                                                            
            -- writes an `*' at column `x' , row `y'                
            function mark (x,y)                                     
              io.write(string.format("\27[%d;%dH*", y, x))          
            end                                                     
                                                            
            -- Terminal size                                        
            TermSize = {w = 80, h = 24}                             
                                                                    
            -- plot a function                                      
            -- (assume that domain and image are in the range [-1,1])
            function plot (f)                                       
              eraseTerminal()                                       
              for i=1,TermSize.w do                                 
                 local x = (i/TermSize.w)*2 - 1                     
                 local y = (f(x) + 1)/2 * TermSize.h                
                 mark(i, y)                                         
              end                                                   
              io.read()  -- wait before spoiling the screen         
            end                                                     
 要想让这个例子正确的运行,你必须调整你的终端类型和代码中的控制符一致
  plot(function (x) return math.sin(x*2*math.pi) end)
 将在屏幕上输出一个正弦曲线.
 将第一类值函数应用在表中是Lua实现面向对象和包机制的关键,这部分内容在后面章节介绍
6.1 闭包
 当一个函数内部嵌套以一个函数定义时,内部的函数体可以访问外部的函数的局部变量,这种特征我们称作词法定界.虽然这看起来很清楚,事实并非如此,词法定界加上第一类函数在编程语言里是一个功能强大的概念,很少语言提供这种支持.
 下面看一个简单的例子,假定有一个学生姓名的列表和一个学生名和成绩对应的表;现在想根据学生的成绩从高到低对学生进行排序,可以这样做:
 names = {"Peter", "Paul", "Mary"}
    grades = {Mary = 10, Paul = 7, Peter = 8}
    table.sort(names, function (n1, n2)
      return grades[n1] > grades[n2]    -- compare the grades
    end)
 假定创建一个函数实现此功能:
 function sortbygrade (names, grades)
      table.sort(names, function (n1, n2)
        return grades[n1] > grades[n2]    -- compare the grades
      end)
    end
 例子中包含在sortbygrade函数内部的sort中的匿名函数可以访问sortbygrade的参数grades,在匿名函数内部grades不是全局变量也不是局部变量,我们称作外部的局部变量(external local variable)或者upvalue.(upvalue意思有些误导,然而在Lua中他的存在有历史的根源,还有他比起external local variable简短).
 看下面的代码 :
 function newCounter ()
      local i = 0
      return function ()   -- anonymous function
               i = i + 1
               return i
             end
    end
   
    c1 = newCounter()
    print(c1())  --> 1
    print(c1())  --> 2
 匿名函数使用upvalue i保存他的计数,当我们调用匿名函数的时候i已经超出了作用范围,因为创建i的函数newCounter已经返回了.然而Lua用闭包的思想正确处理了这种情况.简单的说闭包是一个函数加上它可以正确访问的upvalues.如果我们再次调用newCounter,将创建一个新的局部变量i,因此我们得到了一个作用在新的变量i上的新闭包.
 c2 = newCounter()
    print(c2())  --> 1
    print(c1())  --> 3
    print(c2())  --> 2
 c1,c2是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包.
 技术上来讲,闭包指值而不是指函数,函数仅仅是闭包的一个原型声明;尽管如此,在不会导致混淆的情况下我们继续使用术语函数代指闭包.
 闭包在上下文环境中提供很有用的功能,如前面我们见到的可以作为高级函数(sort)的参数;作为函数嵌套的函数(newCounter).这一机制使得我们可以在Lua的函数世界里组合出奇幻的编程技术.闭包也可用在回调函数中,比如在GUI环境中你需要创建一系列button,但用户按下button时回调函数被调用,可能不同的按钮被按下时需要处理的任务有点区别.具体来讲,一个十进制计算器需要10个相似的按钮,每个按钮对应一个数字,可以使用下面的函数创建他们:
 function digitButton (digit)
      return Button{ label = digit,
                     action = function ()
                                add_to_display(digit)
                              end
                   }
    end
 这个例子中我们假定Button是一个用来创建新按钮的工具,label是按钮的标签,action是按钮被按下时调用的回调函数.(实际上是一个闭包,因为他访问upvalue digit).digitButton完成任

[1] [2]  下一页


Tags:Programming,in,Lua,翻译,More,about,Functions
[数据载入中...] [返回上一页] [打 印]