深入函数 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 语句到另一个函数

lua学习之深入函数第二篇的更多相关文章

  1. lua学习之深入函数第一篇

    深入函数第一篇 函数是第一类值,具有特定的词法域 第一类值 第一类值的意思是函数与 lua 中的其他类型如数字,字符串具有相同的权力 函数可以存储到全局变量或局部变量变量,还可以存储到 table 中 ...

  2. lua学习之类型与值篇

    类型与值 lua 是动态类型的语言 在语言中没有类型定义的语法 每个值都携带有它的类型信息 8种基础类型 用 type 可以返回这个值的类型的名称 将一个变量用于不同类型,通常会导致混乱的代码 但合理 ...

  3. Python学习之路【第二篇】-pyc简介、Python常用的数据类型及其用法和常用运算符

    1.pyc简介 python程序在运行时也有编译过程,编译后会产生.pyc文件.这是一种由python虚拟机执行的二进制文件(字节码),用于保存内存中PyCodeObject,以便加快程序的加载运行. ...

  4. Lua学习笔记3. 函数可变参数和运算符、转义字符串、数组

    1. Lua函数可以接受变长数目的参数,和C语言类似,在函数的参数列表中使用(...)表示函数可以接受变长参数 lua函数将参数存放在一个table中,例如arg,那么#arg可以获得参数的个数 fu ...

  5. 深入学习jQuery选择器系列第二篇——过滤选择器之子元素选择器

    × 目录 [1]通用形式 [2]反向形式 [3]首尾元素 [4]唯一元素 前面的话 在上一篇中已经介绍过基础选择器和层级选择器,本文开始介绍过滤选择器.过滤选择器是jQuery选择器中最为庞大也是最为 ...

  6. python之函数第二篇

    一.名称空间与作用域 名称空间分类: 内置名称空间 import this dir(buil-in) 查看全部内置 全局名称空间 局部名称空间 在函数体内等 查询全局和局部 globals()方法可以 ...

  7. [知了堂学习笔记]_css3特效第二篇--行走的线条&&置顶导航栏

    一.行走的线条. 效果图(加载可能会慢一点儿,请稍等...): html代码: <div class="movingLines"> <img src=" ...

  8. 马上运行函数-$(function(){})篇

    QQ:1187362408 欢迎技术交流和学习 马上运行函数-$(function(){})篇(jquery): TODO: 1.jquery:jQuery(function($){ }) 与 $(d ...

  9. 学习KnockOut第二篇之Counter

                                                                        学习KnockOut第二篇之Counter        欲看此 ...

随机推荐

  1. 基于iTextSharp的PDF操作(PDF打印,PDF下载)

    基于iTextSharp的PDF操作(PDF打印,PDF下载) 准备 1. iTextSharp的简介 iTextSharp是一个移植于java平台的iText项目,被封装成c#的组件来用于C#生成P ...

  2. 玩转Django2.0---Django笔记建站基础九(一)(Auth认证系统)

    第九章 Auth认证系统 Django除了有强大的Admin管理系统之外,还提供了完善的用户管理系统.整个用户管理系统可分为三大部分:用户信息.用户权限和用户组,在数据库中分别对应数据表auth_us ...

  3. Ninject 初步 -Getting Started with Ninject 精通ASP-NET-MVC-5-弗瑞曼 Listing 6-10

  4. CentOS7安装MySQL、Tomcat和GitBlit记录

    一.安装MySQL 1.安装这个发布包 yum localinstall mysql-community-release-el6-5.noarch.rpm 可以通过下面的命令来确认这个仓库被成功添加: ...

  5. Java多线程,对锁机制的进一步分析

    1 可重入锁 可重入锁,也叫递归锁.它有两层含义,第一,当一个线程在外层函数得到可重入锁后,能直接递归地调用该函数,第二,同一线程在外层函数获得可重入锁后,内层函数可以直接获取该锁对应其它代码的控制权 ...

  6. UML--->活动图梳理

    活动图梳理 活动图 概述 活动图是UML用于对系统的动态行为建模的另一种常用工具,它描述活动的顺序,展现从一个活动到另一个活动的控制流,常常用于描述业务过程和并行处理过程,活动图在本质上是一种流程图. ...

  7. 学习记录(Python列表)

    列表(List)是Python语言中最通用的序列数据结构之一,列表是一个没有固定长度的,用来表示任意类型对象的位置相关的有序集合.列表中的数据项不需要具有相同的数据类型 列表的基本操作: 1.创建列表 ...

  8. ros之自定义服务数据

    如何自定义服务数据 string name Uint8 age Uint8 sex Uint8 unknown =0 Uint8 male =1 Uint8 female = 2 (上面的是Reque ...

  9. c++中静态成员函数

    静态成员函数 静态成函数和静态成员数据相同,他们都属于某一个类的静态成员而不是某一个对象的成员. 静态数据成员的使用方法和注意事项 1.静态数据成员在定义或说明时前面加上关键字static 2.初始化 ...

  10. struts.xml头部代码

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "- ...