Programming in Lua翻译--More about Functions
翻译本文章是个人爱好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完成任
Tags:Programming,in,Lua,翻译,More,about,Functions

