《Lua程序设计》之 闭包

九、闭包

9.1 函数是第一类值

第一类值(first-class value)意味着Lua语言中的函数与其他常见类型的值(如数值和字符串)有同等权限,即函数既可以保存在变量中、表中,又可以作为参数、返回值等,如:

a = {p = print}
a.p("Hello World")
print = math.sin
a.p(print(1))
math.sin = a.p
math.sin(10,20)

--打印
Hello World
0.8414709848079
10  20

函数作为值的一种,定义方式如下:

function foo(x) return 2*x end
--等价于
foo = function(x) return 2*x end --右边就是函数构造器,与表构造器{}相似

看匿名函数作为参数的使用场景

network = {
    {name = 'grauna'},
    {name = 'arraial'},
    {name = 'lua'},
    {name = 'derain'}
}
table.sort( network, function(a,b) return(a.name > b.name) end)

for i,t in pairs(network) do
    for k,v in pairs(t) do
        print(k,v)
    end
end

--print
name    lua
name    grauna
name    derain
name    arraial

像函数sort这样以另一个函数为参数的函数,我们称之为高阶函数(higher-order function)

9.2 非全局函数

我们先来模拟一下io.read和math.sin这种存储在表中的函数的实现

Lib = {
    foo = function(x,y) return x+y end,
    goo = function(x,y) return x-y end
}
print(Lib.foo(10,6))
print(Lib.goo(10,6))

Lib = {}
function Lib.foo(x,y) return x+y end
function Lib.goo(x,y) return x-y end

以上两种写法都可以

当把一个函数存储到局部变量时,就得到一个局部函数,局部函数对于包(package)而言尤其有用。

local function f(param)
    body
end

但是对于迭代函数,我们看下下面的局部函数使用是否有问题:

local fact = function (n)
    if n==0 then return 1 
    else return n*fact(n-1)
    end
end

fact(10)

运行的时候会发现报test.lua:3: attempt to call global 'fact' (a nil value)的错,也就是在使用fact函数的时候,fact都还没赋值,调用全局的fact,而全局fact并未定义,所以会报该错,我们改一下代码

local fact
fact = function (n)
    if n==0 then return 1 
    else return n*fact(n-1)
    end
end

print(fact(10))

这样就没问题了,所以局部函数涉及到迭代的时候应该按上面的方法定义

local f
f = function (param)
    body
end

对于间接递归函数,必须要使用与明确的前向声明等价的形式

local f --“前向” 声明

local function g ( )
    some code f() some code
end

function f()   --这里的f是上面声明的f,不需要再用local修饰
    some code g() some code
end
9.2 词法定界

当编写一个被其他函数B包含的函数A时,被包含的函数A可以访问包含其的函数B的所有局部变量,我们将这种特性称为词法定界(lexical scoping)。看例子

function sortbygrade(names,grades)
    table.sort(names,function (name1,name2) return grades[name1] > grades[name2] end )
    for i,v in ipairs(names) do
        print(i,v)
    end
end

names = {"Peter","Paul","Mary"}
grades = {Mary = 10,Paul = 7,Peter = 8}
sortbygrade(names,grades)

我们看到table.sort的第二个参数匿名函数,可以访问grades,而grades是sortbygrade的形参,在该匿名函数中,这个grades就是我们所说的非局部变量。
实际上匿名函数就是一个闭包,一个闭包就是一个函数外加能够使用该函数正确访问非局部变量所需的其他机制,看以下代码

function newCounter()
    local count = 0
    return function ()
            count = count + 1
            return count
           end
end

c1 = newCounter()
c2 = newCounter()
print(c1())         --1
print(c1())         --2
print(c2())         --1
print(c1())         --3
print(c2())         --2

调用newCounter都会生成一个新的闭包,每个闭包都有自己的非局部变量count,保存着count的值,是不是很神奇。从技术上讲,Lua语言是没有函数的,只有闭包,函数本身只是闭包的一种原型。

接下来我们来创建安全的运行时环境,即所谓的沙盒,当执行一些诸如从远程服务器上下载到的未受信任代码时,安全的运行时环境非常重要。例如,我们可以通过使用闭包重定义函数io.open来限制一个程序能够访问的文件

do              -- do 限制局部变量oldOpen的作用范围
    local oldOpen = io.open 
    local access_OK = function (filename,mode)
        check access
    end
    io.open = function (filename,mode)
        if access_OK(filename,mode) then
            return oldOpen(filename,mode)
        else
            return nil,"access denied"
        end
    end
end

这个跟iOS魔法盒功能一样(method swizzing),但是iOS写起来很麻烦,这个轻便很舒服,通过这一技巧,就可以在保证简洁性和灵活性的前提下在Lua语言本身构建Lua沙盒。相对于提供一套大而全的解决方案,Lua语言提供的是一套“元机制”。很棒!Lua的魅力越来越显示出来了。

9.3 小试函数式编程

好多代码,关于几何的看的脑壳疼,不写了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容