lua函数具有两大特征:函数作为第一类值,函数具有特定的词法域(Lexical Scoping)

所谓第一类值:代表函数和其他传统类型的值是等价的(例如数字和字符串),函数可以同他们一样存储在变量,table中,可以作为实参传递,可以作为函数返回值。

对于第一类值,需要讲明,函数和其他值一样都是匿名的,是没有名字的。而我们平时所说的函数名,如print(),都只是一种语法糖,一个持有某个函数的变量。

例如:

function foo(x) return 2*x end                           --->等价于                                  foo = function(x)   return 2*x end

函数定义实际上是一条赋值语句。这条语句先创建一个类型为函数的值,然后将其赋值给一个变量。

我们将表达式“function (x) <body> end”视为函数的构造式,又被称其为匿名函数,在某些特殊情况下,我们会用到匿名函数。

特定的词法域(Lexical Scoping):指一个函数可以嵌套在另一个函数中,内部的函数可以访问外部函数的变量。

闭合函数(closure)

由于词法域的原因,当一个函数写在另一个函数内部,而这个内部函数可以访问外部函数的局部变量,这个被访问的局部变量既不是全局的也不是局部的,被称为非局部的变量(non-local variable).

所以闭合函数就是这个内部函数加上该函数需要访问的所有的非局部变量的函数。

关于闭包的实现原理,推荐以下博客http://blog.csdn.net/maximuszhou/article/details/44280109。

关于闭包,有几个需要注意的地方:

e.g.

function newCounter()local i = 0;return function()i = i+1return iend
endc = newCounter()   -- newCounter返回匿名函数赋予变量c,此时c是一个闭包,同时拥有一个upvalue值
print(c())   -->1
print(c())   -->2      --匿名函数再次访问闭包中的upvalue值,使其加1.--注意,如果再次调用newCounter,它将创建一个新的闭包,该闭包将拥有一个新的upvalue.
c1 = newCounter()    -- 返回新的闭包,赋值给变量c1.--同时需要注意:如果一个函数内部,有多个闭合函数,这些闭合函数如果需要访问同一个外部变量,那么这个upvalue在这些闭包之间是共享的。、
--e.g.
function f()local i = 0local function foo1()i = i +1return print(i)endlocal function foo2()i = i+10return print(i)endreturn foo1, foo2
end
t1,t2 = f()             --   t1,t2闭包函数共享i这个upvalue值。
t1()                    -->1
t2()                    -->11

闭包有很多作用,主要体现在,当上一级函数的局部变量被释放后,还可以通过闭包函数的upvalue值获的访问。

非局部的函数(non-local function)
函数不仅可以存储在全局变量中,还可以存储在table字段和局部变量中。因为函数是第一类值
table字段中:
lib = {}
lib.foo = function(x,y) return x+y end
lib.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}
和这种方式:
lib = {}
function lib.foo(x,y) return x+y end
function lib.goo(x,y) return x-y end
将函数存储在局部变量中,便得到一个局部函数。局部函数只能在特定的作用域中使用。
lua将每个程序块(chunk)作为一个函数来处理,所以里面声明的函数就是局部函数,只在该程序块中可见。词法域保证了程序包中的其他函数可以使用这些局部函数
比如:
local f = function ()
<函数体>
end
local g = function()
<代码>
f()                    --   f()在这里可见
<代码>
end
对于局部函数的定义,lua也提供一种语法糖:
loacal function f()
<函数体>
end
注意,当Lua在展开这种语法糖时,并不使用基本函数定义语法。而是;将其展开为以下这种:
local f
f = function() <函数体> end      --对比基本函数定义语法的不同:local f = function() <函数体> end
lua这样做,是有用处的。
比如在定义递归的局部函数时,如果采用了基本函数定义语法,大多是错误的:
local fact = function(n)
if n == 0 then return 1
else return n* fact(n-1)      -- 错误
end
end
当lua编译到fact(n-1)时,由于局部变量fact尚未完成定义,所以这句表达式最终调用了一个全局的fact,而非此函数本身。
为了解决这个问题,可以先定义一个局部变量,然后再定义函数本身:
local fact
fact = function()
if n==0 then return 1
else return n*fact(n-1)
end
end
现在,fact()的调用就表示了局部变量,即使在定义时,这个局部变量的值还没有完成定义,但在执行时,fact已经拥有了正确的值。
当然解决方法,也可以用上面提到的语法糖,因为lua对其的展开并不是使用基本函数定义语法。
so:
local function fact(n)
if n == 0 then return 1
else return n* fact(n-1)
end
end
这种方式完全等价于上面。
这个技巧对于间接递归的函数是无效的,在这种情况中,必须使用明确的前向声明(forward declaration)
local f, g        --明确的前向声明
function g()
<code>f()<code>
end
function f()
<code>g()<code>
end
注意,别把第二个函数定义写为“local function f”.那么lua会创建一个新的local f,而原来声明的f将置于未定义状态。
正确的尾调用(proper tail call)
lua还支持尾调用消除(tail-call elimination)这一特性。
尾调用:当一个函数是另一个函数的最后一个动作时,该调用就是一条尾调用。
尾调用消除:当在进行尾调用时不消耗任何栈空间,这种实现就称为尾调用消除。
e.g.
function f(x) return g(x) end
当程序执行完g(x)之后,程序就不需要回到f()这个函数了,因为已经无事情可做了,这时程序也不需要保存任何关于f()函数的栈信息了。当g返回,程序的控制权直接返回到调用f的那个点上。
简单来说,就是程序在进入g(x)后,上一级函数的栈空间被完全释放,从而节省了内存。
正是这个尾调用消除特性,使得一个程序可以拥有无数嵌套的尾调用,而不会造成栈溢出。
function foo(n)
if n>0 then return foo(n-1) end
end
当然要享受尾调用消除这个特性,就必须保证这个调用时尾调用。判断的标准就是,在调用玩这个函数之后,上一级的函数就无事情可做了。
比如以下一些错我例子:
funtion f(x) g(x) end            -- 调用完g后,还需要返回f,丢弃g返回的临时结果。
return  g(x) + 1        -- 还要做一次加法
return x or g(x)        -- 还要将其调整为一个返回值
return (g(x))        -- 还要将其调整为一个返回值
只有这种形式的调用才是正确的:return <func>(<args>)   .
由于一条尾调用就好比一条goto语句,所以尾调用的一大应用就是编写状态机(state machine)

lua--函数深入:闭合函数,局部函数,尾调用相关推荐

  1. Lua程序设计 | 字符串、表、函数与IO

    From<Programming in Lua> by Roberto Ierusalimschy 文章目录 字符串 字符串常量 长字符串/多行字符串 强制类型转换 字符串标准库 表 表索 ...

  2. ES6 尾调用和尾递归优化

    尾调用 尾调用(Tail Call)是函数式编程的一个重要概念,就是指某个函数的最后一步是调用另一个函数. function fun(x){return a(x); } 上面代码中,函数fun的最后一 ...

  3. Lua 函数、闭包、尾调用总结

    <lua 程序设计>在线阅读:http://book.luaer.cn/ 1.函数 函数有两种用途: 完成指定的任务,这种情况下函数作为调用语句使用: 计算并返回值,这种情况下函数作为赋值 ...

  4. 查看某个方法在哪里被调用_MATLAB局部函数公有化的方法: localfunctions

    知乎视频​www.zhihu.com MATLAB的一个函数文件里面, 开头第一个函数是可以被外部调用的, 而其他函数是无法直接被外部调用的, MATLAB称之为局部函数. 如果用OOP的术语来说, ...

  5. 递归循环一个无限极数组_理解递归、尾调用优化和蹦床函数优化

    想要理解递归,您必须先理解递归.开个玩笑罢了, 递归 是一种编程技巧,它可以让函数在不使用 for 或 while 的情况下,使用一个调用自身的函数来实现循环. 例子 1:整数总和 例如,假设我们想要 ...

  6. Dotnet的局部函数和委托的对比

    上一篇说了一下委托,这篇来说说局部函数和委托的对比.   把委托和局部函数放成前后篇,是因为这两个内容很像,用起来容易混. 需要了解委托相关内容,可以看这一篇 [传送门]   使用委托表达式(Lamb ...

  7. do语句转化为局部函数一例

    do: (do ((x a (b x))           (y c (d y)))          ((test x y) (z x y))        (f x y)) 局部函数: (lab ...

  8. 原来 JS 也支持跟 Lua 语意一样的内嵌函数的闭包概念

    原来 JS 也支持跟 Lua 语意一样的内嵌函数的闭包概念. 我是从这里看来的: http://blog.dreambrook.com/soloist/archive/2005/03/13/526.a ...

  9. python函数局部变量_Python局部函数– functoolspartial()

    python函数局部变量 什么是Python局部函数? (What is a Python Partial Function?) Sometimes a function accepts multip ...

最新文章

  1. Java相对路径/绝对路径总结(转)
  2. php-fpm 没有启动脚本,php-fpm服务启动脚本
  3. Velocity 页面加减运算
  4. C++11 多线程库使用说明
  5. MySQL 数据库添加数据时为什么会产生外码(外键)约束?原理就是什么?如何解决?
  6. golang实现四种排序(快速,冒泡,插入,选择)
  7. 电脑不识别移动硬盘怎么办_U盘插入电脑后不识别无法读取的解决方法
  8. 拓端tecdat|R语言混合效应逻辑回归(mixed effects logistic)模型分析肺癌数据
  9. Android发短信功能
  10. SpringBoot项目 四种读取properties文件的方式
  11. 算法学习-求平方根函数
  12. retrofit简单的网络请求
  13. Tableau实战 Tableau官网各版块访问情况(四)各网址情况分析
  14. Matplotlib_2
  15. 电脑远程控制,自动重启,断电重启,网络自动连接
  16. Heritrix3.0
  17. Linux之常见的通配符
  18. centos7 moloch安装及优化
  19. Arduino超声波测距模块控制蜂鸣器
  20. 怎么放大图片保持清晰度?如何把图片无损放大?

热门文章

  1. js设置和获取html和文本,JS---DOM---设置和获取---标签内容和文本内容
  2. 怀旧服10月3日服务器维护,魔兽怀旧服:即将开放怀旧服PTR服务器 3/10开放阿拉希...
  3. 2018年最新版微信公众号本地测试方案
  4. 使用element ui select下拉框多选,编辑状态下回显数据
  5. 阳光厨房——软件需求工程与UML建模
  6. 苹果手机看html文件效果,如何在ios手机端的Safari浏览器中“查看网页源代码”...
  7. HTML中容器(div)动态加载HTML页面的方法
  8. 字符串逆序(递归实现)
  9. 【Spring】Spring底层核心原理解析
  10. 论文学习——分段时间序列相似性研究与应用