Lua函数之二
Lua函数之二
Lua中函数的两个重要特性:
1、函数和其他类型(如number、string)一样,可以存放在变量中,也可以存放在table中,可以作为函数的参数,还可以作为函数的返回值。
2、嵌套的函数可以访问其外部函数中的局部变量——闭包。
例如:
- local foo = function(x) return x^ end -- 函数构造式
- local function foo(x) return x^ end -- 函数定义
第1种方法创建一个函数,并赋一个变量;
第2种方法其实是一种特例。
1、非全局函数
Lua中的函数可以作为全局变量,也可以作为局部变量。
将函数保存在一个局部变量时,得到一个局部函数:
- local f = function(...)
- ...
- end
- local g = function(...)
- ...
- f() -- external local 'f' is visible here
- ...
- end
函数作为table的域,这种情况下,必须注意函数和表语法:
第1种方式:
- lib = {add = function(x,y) return x+y end, sub = function(x,y) return x-y end}
第2种方式:
- lib = {}
- lib.add = function(x,y) return x+y end
- lib.sub = function(x,y) return x-y end
第3种方式:
- lib = {}
- function lib.add(x,y) return x+y end
- function lib.sub(x,y) return x-y end
调用方式:
- lib.add(x,y)
2、尾调用
尾调用(tail recursion)是指函数最后一个动作是调用另外一个函数,例如
- function f(x)
- return g(x) // 尾调用
- end
这种情况下,当被调用函数g结束时程序不需要返回到调用者f,所以尾调用之后程序不需要在栈中保留关于调用者的任何信息。
Lua解释器利用这个特性在处理尾调用时不使用额外的栈,那么尾调用递归的层次是可以无限制的。例如:
- function foo (n)
- if n > then return foo(n-) end
- end
不管n的值有多大,都不会发生栈溢出。
注意:下面的返回方法不是尾调用:
- return g(x)+ -- must do the addition
- return x or g(x) -- must adjust to 1 result
- return (g(x)) -- must adjust to 1 result
3、闭包
闭包:当一个函数内部嵌套另一个函数定义时,内部的函数体可以访问外部函数的局部变量。
例如:
- function foo()
- local i =
- return function() i = i + return i end
- end
- c1 = foo()
- c2 = foo()
- print(c1()) --
- print(c1()) --
- print(c2()) --
例子中foo函数的内部匿名函数可以访问foo的局部变量i,在匿名函数内部i是external local variable(也称作upvalue)。
c1和c2是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包。简单的说,闭包是一个匿名函数及其upvalues 。
4、迭代器
迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。
在Lua中使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。
迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,闭包提供的机制可以很容易实现这个任务。
闭包是一个内部函数,它可以访问一个或者多个外部函数的external local variable,每次闭包的成功调用后,这些upvalues都会保存他们当前的状态。
一个典型的闭包结构包含两个函数:一个是闭包自己,另一个是工厂(创建闭包的外部函数)。
用闭包实现数组迭代器的例子:
- function list_iter(t)
- local i=
- local n=#t
- return function()
- i=i+
- if i<=n then return t[i] end
- end
- end
- a={,,}
- iter=list_iter(a) -- 创建迭代器,保存的外部局部变量(t,i,n)
- while true do
- local element=iter()
- if element==nil then break end
- print(element)
- end
上面设计的迭代器也可以适用于泛型for循环:
- for element in list_iter(a) do
- print(element)
- end
泛型for循环首先调用迭代工厂,并在内部保留迭代函数,因此我们不需要iter变量;然后在每一个新的迭代处调用迭代器函数,当迭代器返回nil时循环结束。
另一个例子,逐行遍历所有的单词:
- function allwords()
- local line = io.read()
- local pos =
- return function()
- while line do
- local s,e=string.find(line, "%w+", pos)
- if s then
- pos = e+
- return string.sub(line, s, e)
- else
- line=io.read()
- pos=
- end
- end
- return nil
- end
- end
- for word in allwords() do
- print(word)
- end
上面提到的迭代器的名字可能有些误导,因为它并没有迭代,完成迭代功能的是for循环语句,也许更好的叫法应该是生成器(generator)。
下面是一个真正的迭代器的例子,它在内部完成了循环,这样我们使用迭代器的时候就不需要再使用循环了,迭代器仅接受一个函数作为参数,并且这个函数在迭代器内部被调用。
- function allwords(f)
- for l in io.lines() do
- for w in string.gfind(l, "%w+") do
- f(w)
- end
- end
- end
- allwords(print)
更一般的做法是使用匿名函数作为参数,下面的例子打印出单词"hello"出现的次数:
- local count =
- allwords(function(w)
- if w=="hello" then count=count+ end
- end )
- print(count)
5、泛型for的语义
泛型for在自己内部保存迭代函数,包括三个值:迭代函数、状态常量、控制变量。
- for <var-list> in <exp-list> do
- <body>
- end
其中,<var-list>是一个或多个逗号分隔的变量名列表,且第一个变量为控制变量,其值为nil时循环结束;
<exp-list>是以一个或多个逗号分隔的表达式列表,通常情况下exp-list只有一个值:迭代工厂的调用。
泛型for的执行过程:
1、初始化,计算in后面表达式的值,表达式应该返回三个值:迭代函数、状态常量、控制变量;
2、将状态常量和控制变量作为参数调用迭代函数;
3、将迭代函数返回值赋给变量列表;
4、如果控制变量为nil,循环结束;
5、回到第2步;
LUA标准库提供了几种迭代器,包括迭代文件每行的(io.lines),迭代table元素的(pairs),迭代数组元素的(ipairs),迭代字符串中单词的(string.gmatch)等。
以iparis为例,实现如下:
- function iter(a, i)
- i = i +
- local v=a[i]
- if v then return i,v end
- end
- function ipairs(a)
- return iter, a,
- end
当泛型for循环调用iparis(a)开始循环时,它获取三个值:迭代函数iter、状态常量a、控制变量初始值0;
然后调用iter(a,0)返回1,a[1],第二次迭代调用iter(a,1)返回2,a[2],...,直到第一个非nil元素。
pairs函数的实现可以利用next方法:
- function pairs(t)
- return next,t,nil
- end
也可以直接使用next方法来迭代一个table:
- lst = {,,x="bj",,"chen", ,y="qi"}
- for k,v in next,lst do
- print(k,v)
- end
OK,上文提到的iterator都是无状态的迭代器,每一次迭代,迭代函数都是用两个常量(状态常量和控制变量)的值作为参数被调用。
在有些情况下,迭代器需要保存多个状态信息而不是简单的状态常量和控制变量,最简单的方法是使用闭包,
还有一种方法是将所有的状态信息封装到table内,将table作为迭代器的状态常量。
allwords的另一个实现版本:
- local iterator
- function allwords()
- local state={line=io.read(), pos=}
- return iterator, state
- end
- function iterator(state)
- while state.line do
- local s,e=string.find(state.line, "%w+", state.pos)
- if s then
- state.pos = e+
- return string.sub(state.line, s,e)
- else
- state.line = io.read()
- state.pos =
- end
- end
- return nil
- end
Lua函数之二的更多相关文章
- lua入门之二:c/c++ 调用lua及多个函数返回值的获取
当 Lua 调用 C 函数的时候,使用和 C 调用 Lua 同样类型的栈来交互. C 函数从栈中获取她的參数.调用结束后将返回结果放到栈中.为了区分返回结果和栈中的其它的值,每一个 C 函数还会返回结 ...
- lua堆栈操作常用函数学习二
/* ** basic stack manipulation */ LUA_API int <strong> (lua_gettop) (lua_State *L); </str ...
- Lua函数以及闭合函数的理解
Lua函数以及闭合函数的理解 来源 http://blog.csdn.net/mydad353193052/article/details/48731467 词法域和第一类型 在C/C++,C#或者J ...
- Lua面向对象之二:类继承
1.类继承 ①代码 Sharp = { } --① 父类 function Sharp:new() local new_sharp = { } self.__index = self --②,self ...
- Step By Step(Lua函数)
Step By Step(Lua函数) 一.函数: 在Lua中函数的调用方式和C语言基本相同,如:print("Hello World")和a = add(x, y).唯一的 ...
- Javascript常用方法函数收集(二)
Javascript常用方法函数收集(二) 31.判断是否Touch屏幕 function isTouchScreen(){ return (('ontouchstart' in window) || ...
- C中调用Lua函数
我们先来看一个简单的例子: lua_State* L = NULL; // 内部调用lua函数 double f(double x, double y) { double z; lua_getglob ...
- Lua函数之一
LUA函数之一 函数声明: function foo(arguments) statements end 1.函数调用 调用函数的时候,如果参数列表为空,必须使用()表明是函数调用,例如: os.da ...
- 一些LUA函数(转载)
转自http://hi.baidu.com/chevallet/item/9a3a6410c20d929198ce3363 一些LUA函数 1.assert (v [, message]) 功能:相当 ...
随机推荐
- 快钱支付与Sql Server的乐观锁和悲观锁
在实际的多用户并发访问的生产环境里边,我们经常要尽可能的保持数据的一致性.而其中最典型的例子就是我们从表里边读取数据,检查验证后对数据进行修改,然后写回到数据库中.在读取和写入的过程中,如果在多用户并 ...
- WCF与ASMX Web服务差异比较[译]
First of all, it needs to understand that WCF Service provides all the capabilities of .NET web serv ...
- 北京联想招聘-Android Framework高级工程师(7-10年) 加入qq 群:220486180 或者直接在此 留言咨询
Job ID #: 45038 Position Title: Android Framework高级工程师 Location: CHN-Beijing Functional Area: Resear ...
- 解答WPF中ComboBox SelectedItem Binding不上的Bug
正在做一个打印机列表,从中选择一个打印机(System.Printing) <ComboBox Width="150" ItemsSource="{Binding ...
- Mysqli基础知识
相信原来在开始学习php的时候,很多人使用的数据库首选MySQL,连接数据库的扩展首选mysql扩展,但随着php版本的提高,mysql扩展正逐渐被mysqli和PDO所取代.正如使用mysql函数时 ...
- 我为什么期待M#?
前段时间的报导"微软将推新编程语言M#:系统编程级别的C#",第一眼看到并没有当初看到F#的那一种不安,反而感到欣喜,业界一直存在"语言论"讨论c#.java. ...
- MongoDB查询并更新一粟
//更新操作使用collection的Update方法,有泛型和非泛型两个版本: //其签名如下(列出了两个简单并常用的的重载,还有几个): public virtual WriteConcernRe ...
- iOS UI基础-17.0 UILable之NSMutableAttributedString
在iOS开发中,常常会有一段文字显示不同的颜色和字体,或者给某几个文字加删除线或下划线的需求.之前在网上找了一些资料,有的是重绘UILabel的textLayer,有的是用html5实现的,都比较麻烦 ...
- loaded the "XXXView" nib but the view outlet was not set 解决方案
'-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "XXXView" nib but the view o ...
- Sublime-jQueryDocs
Package Control Messages======================== jQueryDocs---------- This package shows a selected ...