Lua函数之二

Lua中函数的两个重要特性:

1、函数和其他类型(如number、string)一样,可以存放在变量中,也可以存放在table中,可以作为函数的参数,还可以作为函数的返回值。

2、嵌套的函数可以访问其外部函数中的局部变量——闭包。

例如:

local foo = function(x) return x^2 end          -- 函数构造式

local function foo(x) return x^2 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 > 0 then return foo(n-1) end
end

不管n的值有多大,都不会发生栈溢出。

注意:下面的返回方法不是尾调用:

return g(x)+1        -- 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 = 0

    return function() i = i + 1 return i end

end

c1 = foo()
c2 = foo()

print(c1())      -- 1
print(c1())      -- 2
print(c2())      -- 1

例子中foo函数的内部匿名函数可以访问foo的局部变量i,在匿名函数内部i是external local variable(也称作upvalue)。

c1和c2是建立在同一个函数上,但作用在同一个局部变量的不同实例上的两个不同的闭包。简单的说,闭包是一个匿名函数及其upvalues 。

4、迭代器

迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素。

在Lua中使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素。

迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,闭包提供的机制可以很容易实现这个任务。

闭包是一个内部函数,它可以访问一个或者多个外部函数的external local variable,每次闭包的成功调用后,这些upvalues都会保存他们当前的状态。

一个典型的闭包结构包含两个函数:一个是闭包自己,另一个是工厂(创建闭包的外部函数)。

用闭包实现数组迭代器的例子:

function list_iter(t)
    local i=0
    local n=#t

    return function()
        i=i+1
        if i<=n then return t[i] end
    end
end 

a={1,2,3}

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 = 1

    return function()
        while line do
            local s,e=string.find(line, "%w+", pos)
            if s then
                pos = e+1
                return string.sub(line, s, e)
            else
                line=io.read()
                pos=1
            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 = 0
allwords(function(w)
        if w=="hello" then count=count+1 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 + 1
    local v=a[i]
    if v then return i,v end
end 

function ipairs(a)
    return iter, a, 0
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 = {100,250,x="bj",37,"chen", 4,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=1}
    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+1
            return string.sub(state.line, s,e)
        else
            state.line = io.read()
            state.pos = 1
        end
    end

    return nil
end 

时间: 2024-10-11 06:48:24

Lua函数之二的相关文章

lua入门之二:c/c++ 调用lua及多个函数返回值的获取

当 Lua 调用 C 函数的时候,使用和 C 调用 Lua 同样类型的栈来交互. C 函数从栈中获取她的參数.调用结束后将返回结果放到栈中.为了区分返回结果和栈中的其它的值,每一个 C 函数还会返回结果的个数(the  function  returns  (in  C)  the  number  of  results  it  is leaving on the stack.). // luacallcpp.cpp : 定义控制台应用程序的入口点. // #include "stdafx.

lua堆栈操作常用函数学习二

/* ** basic stack manipulation */ LUA_API int  <strong> (lua_gettop) (lua_State *L);  </strong><pre class="cpp" name="code"></pre><pre class="cpp" name="code">//返回当前堆栈的大小 LUA_API int lu

04_内置函数(二)

内置函数(二) 1.1 callable() 功能:函数是否可调用 示例1: def f1(): pass f1() f2 = 123 f2() # 输出结果 TypeError: 'int' object is not callable 示例2: def f1(): pass # f1() f2 = 123 # f2() print(callable(f1)) print(callable(f2)) # 输出结果 True False 1.2 chr() 功能:数字转字母,返回对应的ASCII

C语言调用Lua函数

记得上学时,初中英文课本中,上网叫做surfing the internet,中文叫网上冲浪.那个时期,人们经常称互联网为赛博空间.如今工作了,大量的零碎时间用于上微博,知乎,QQ,这些碎片化的阅读让人读起来轻松,也能获取些粗浅的信息.然而它们是消耗时间的黑洞,时间就这样一分一秒地飞逝,年末的时候,知乎会告诉你回答了多少问题,阅读了相当于一部<红楼梦>那么多的文字.只是当你静下来一想,这些浅阅读并没给你带来有深度,系统的知识.在你的时间线上,两条相邻信息往往是八竿子也打不着的.而且你还时不时去

lua函数精讲(一)

函数的用途: 1.完成指定的任务,这种情况下作为调用语句使用. 2.计算并返回值,这种情况下,函数作为赋值语句的表达式使用. 函数的参数为空,必须使用()表示函数调用.例外:当函数只有一个参数,并且这个参数是字符串或表构造时,()是可选的. lua提供了面向对象调用函数的语法 o:foo(x)与o.f(o,x)是等价的. lua函数的实参与形参的匹配与赋值语句类似,多余部分被忽略,缺少部分nil补足. lua函数可以返回多个值,return后列出要返回值的列表即可返回多值 s, e = stri

C中调用Lua函数

我们先来看一个简单的例子: lua_State* L = NULL; // 内部调用lua函数 double f(double x, double y) { double z; lua_getglobal(L, "f"); // 获取lua函数f lua_pushnumber(L, x); // 压入参数x和y lua_pushnumber(L, y); if(lua_pcall(L, 2, 1, 0) != 0) error(L, "error running functi

总结文件操作函数(二)-C语言

格式化读写: #include <stdio.h> int printf(const char *format, ...);                   //相当于fprintf(stdout,format,-); int scanf(const char *format, -); int fprintf(FILE *stream, const char *format, ...);      //中间的参数为写入文件的格式 int fscanf(FILE *stream, const

黑马程序员——c语言学习心得——函数传递二维数组

黑马程序员——c语言学习心得——函数传递二维数组 -------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 一.定义指针的时候一定要初始化.   变量定义的时候给变量初始化,这是保证不出错的一个很好的习惯.尤其是在指针的使用上,如果我们没有给指针初始化,就会出现野指针,该指针的指向并不是我们所希望的,一旦错误的释放了这个指针,就会发生内存的访问.那么如何初始化指针变量呢,一般有以下几种方法:   1.初始化空指针   int* pInteger=N

数组合并函数,二维数组相同字段合并到一起。

一般从数据库中提取数据时,会遇到各种各样类型的数据,要求也不尽相同.自己这两天开发的时候遇到一个很纠结的问题,如下: 比如一个二维数组是这样的: Array ( [0] => Array ( [uid] => 231 [username] => 123456 [active] =>aaaa [transfer] =>1111 ) [1] => Array ( [uid] => 231 [username] =>123456 [active] => bb