zoukankan      html  css  js  c++  java
  • Lua函数之二

    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 
  • 相关阅读:
    Centos开启FTP及用户配置
    mysql update from 子查询
    sql server 查询表某个字段不重复数据
    ASP.NET 获取来源网站的网址,获取上一网页的网址,获取来源网页的URL,获取上一网页的URL
    Warning: Invalid argument supplied for foreach()
    不支持关键字: “userid”。
    apache301重定向设置
    service httpd restart失败解决方法(小记)
    JavaWeb(一)
    jquery中filter的用法
  • 原文地址:https://www.cnblogs.com/chenny7/p/3634381.html
Copyright © 2011-2022 走看看