zoukankan      html  css  js  c++  java
  • lua学习之深入函数第二篇

    深入函数 2

    非全局的函数

    1. 函数是第一类值,函数可以存储到全局变量,局部变量,table 字段中
    2. lua 函数库中的大部分函数存储到 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
    fucntion Lib.goo(x, y) return x - y end
    
    1. 将一个函数存储到一个局部变量中,即为「局部函数」
    2. 该函数只能在对应的作用域使用,对于「程序包」package 很有用
    3. lua 将每一个程序块当作一个函数来处理
    4. 在程序块中声明的函数就是局部函数,只在该程序块中可见
    5. 词法域确保了程序包中的其他函数可以使用这些局部函数。
    local f = function (<参数列表>)
        <函数体>
    end
    
    local g = function (<参数列表>)
        <函数代码>
        f(实参) -- 可以调用 f
    	<函数代码>
    end
    
    local function f(<参数列表>)
        <函数体>
    end
    
    -- 阶乘 n! = n * (n - 1) * (n - 2) * ... 1
    local fact = function (n) -- 错误的递归函数定义
        if n == 0 then 
            return 1
        else
            return n * fact(n - 1) -- fact 函数定义未完成,调用的是 fact 全局变量,而不是 fact 函数本身
        end
    end
        
    -- 正确的递归函数定义
    local fact
    fact = function (n)
        if n == 0 then
            return 1
        else
            return n * fact(n - 1)
        end
    end
    
    local function foo(<参数>) <函数体> end
    -- Lua 将其展开为:
    local foo
    foo = function (<参数>) <函数体> end
    -- 正确的函数定义,对于间接递归无效
    local function fact (n)
        if n == 0 then
            return 1
        else
            return n * fact(n - 1)
        end
    end
    -- 递归就是函数调用函数本身
    -- 间接递归就是 a 函数调用 b 函数而 b 函数又调用了 a 函数
    -- 间接递归需要使用明确的前向声明
    local f, g
    function g ()
        <函数代码>
        f()
        <函数代码>
    end
    
    function f() -- 不要加 local 如果加上那么在函数 g 中引用的就处于未定义状态,因为 lua 会创建一个全新的局部变量 f
        <函数代码>
        g()
        <函数代码>
    end
    

    正确的尾调用

    1. 「尾调用」是类似于 goto 的函数调用
    2. 当一个函数调用是另一个函数的最后一个动作时,该调用就是一条「尾调用」
    function f (x)
        <函数代码>
        return g(x)
    end
    
    1. f 调用完 g 之后就没有执行其他代码了
    2. 在这种情况下,程序就不需要返回「尾调用」所在的函数了
    3. 在「尾调用」之后,程序无需保存任何关于该函数的栈信息
    4. 当 g 返回时,执行控制权可以直接返回调用 f 的那个点上
    5. 使得在进行「尾调用」时不耗费任何栈空间
    6. 这种实现称为「尾调用消除」
    -- 尾调用函数
    function foo(n)
        if n > 0 then
            return foo(n - 1)
        end
    end
    -- 调用完 g 函数后还进行了加法操作,非尾调用
    return g(x) + 1 
    -- 有 or 操作,必须调整为一个返回值
    retrun x or g(x) 
    -- 函数外嵌套一个括号,强制其只返回一个返回值
    return (g(x))
    -- 尾调用标准格式
    return <func>(<args>)
    -- 是一个尾调用
    -- 调用前会对 <func> 及其参数求值
    return x[i].foo(x[j] + a * b, i + j)
    
    

    编写状态机

    1. 典型例子:迷宫
    -- 四间房间的迷宫
    function room1()
        local move = io.read()
        if move == "south" then
            return room3()
        elseif move == "east" then
            return room2()
        else
            print("invalid move")
            return room1()
        end
    end
    
    function room2()
        local move = io.read()
        if move == "south" then
            return room4()
        elseif move == "west" then
            return room1()
        else
            print("invalid move")
            return room2()
        end
    end
    
    function room3()
        local move = io.read()
        if move == "north" then
            return room1()
        elseif move == "east" then
            return room4()
        else
            print("invalid move")
            return room3()
        end
    end
    
    function room4()
        print("congratulations!")
    end
    
    1. 若没有「尾调用消除」,每次用户移动都会创建一个新的栈层,若干步后可能会栈溢出
    2. 「尾调用消除」多用户移动的次数没有任何限制
    3. 因为每次移动实际上只是完成一条 goto 语句到另一个函数
  • 相关阅读:
    Linux关闭防火墙和selinux
    Linux内存VSS,RSS,PSS,USS解析
    JS 将有父子关系的数组转换成树形结构数据
    npm install报错类似于npm WARN tar ENOENT: no such file or directory, open '*** ode_modules.staging***
    react-native之文件上传下载
    Markdown语法简记
    MySQL运维开发
    股票投资
    数据仓库原理与实战
    python基础
  • 原文地址:https://www.cnblogs.com/door-leaf/p/12369006.html
Copyright © 2011-2022 走看看