《Lua程序设计》--学习4

这篇具有很好参考价值的文章主要介绍了《Lua程序设计》--学习4。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

闭包

在Lua语言中,函数是严格遵循词法定界(lexicalscoping)的第一类值(first-classvalue)。

“第一类值”意味着Lua语言中的函数与其他常见类型的值(例如数值和字符串)具有同等权限:一个程序可以将某个函数保存到变量中(全局变量和局部变量均可)或表中,也可以将某个函数作为参数传递给其他函数,还可以将某个函数作为其他函数的返回值返回。

“词法定界”意味着Lua语言中的函数可以访问包含其自身的外部函数中的变量(也意味着Lua语言完全支持Lambda演算)。

函数是第一类值

《Lua程序设计》--学习4

 《Lua程序设计》--学习4

 赋值语句右边的表达式(function(x)body end)就是函数构造器,与表构造器{}相似。因此,函数定义实际上就是创建类型为"function"的值并把它赋值给一个变量的语句。

在Lua语言中,所有的函数都是匿名的(anonymous)

像其他所有的值一样,函数并没有名字。当讨论函数名时,比如print,实际上指的是保存该函数的变量。虽然我们通常会把函数赋值给全局变量,从而看似给函数起了一个名字,但在很多场景下仍然会保留函数的匿名性

《Lua程序设计》--学习4

匿名函数在这条语句中显示出了很好的便利性

像函数sort这样以另一个函数为参数的函数,我们称之为高阶函数(higher-order function)。高阶函数是一种强大的编程机制,而利用匿名函数作为参数正是其灵活性的主要来源

非全局函数

函数不仅可以被存储在全局变量中,还可以被存储在表字段局部变量中。

将函数存储在表字段中,大部分Lua语言的库就采用了这种机制,下列的示例创建了这种函数

《Lua程序设计》--学习4

《Lua程序设计》--学习4

《Lua程序设计》--学习4

 在表字段中存储函数是Lua语言中实现面向对象编程的关键要素。

当把一个函数存储到局部变量时,就得到了一个局部函数(local function),即一个被限定在指定作用域中使用的函数。局部函数对于(package)而言尤其有用:由于Lua语言将每个程序段(chunk)作为一个函数处理,所以在一段程序中声明的函数就是局部函数,这些局部函数只在该程序段中可见。词法定界保证了程序段中的其他函数可以使用这些局部函数。

《Lua程序设计》--学习4

在定义局部递归函数(recursive local function)时,由于原来的方法不适用,所以有一点是极易出错的

《Lua程序设计》--学习4

 当Lua语言编译函数体中的fact(n-1)调用时,局部的fact尚未定义。因此,这个表达式会尝试调用全局的fact而非局部的fact。我们可以通过先定义局部变量再定义函数的方式来解决这个问题:

《Lua程序设计》--学习4

《Lua程序设计》--学习4

所以使用这种定义方式更合适

在间接递归的情况下,必须使用与明确的前向声明(explicit forward declaration)等价的形式:

《Lua程序设计》--学习4

请注意,不能在最后一个函数定义前加上local。否则,Lua语言会创建一个全新的局部变量f,从而使得先前声明的f(函数g中使用的那个)变为未定义状态。

词法定界

当编写一个被其他函数B包含的函数A时,被包含的函数A可以访问包含其的函数B的所有局部变量,我们将这种特性称为词法定界

---[[
function newCounter(  )
	local count = 0
    return function (  )
	  count = count + 1
      return count
    end
end

c1 = newCounter()
print(c1())
print(c1())
print(c1())
c2 = newCounter()
print(c2())
--]]

 《Lua程序设计》--学习4

好怪啊,重复调用的时候那个count还在里面,c1的count是c1的,c2的是c2的,互不干涉

 c1和c2是不同的闭包。它们建立在相同的函数之上,但是各自拥有局部变量count的独立实例。

从技术上讲,Lua语言中只有闭包而没有函数。函数本身只是闭包的一种原型

《Lua程序设计》--学习4

由于函数可以被保存在普通变量中,因此在Lua语言中可以轻松地重新定义函数,甚至是预定义函数。

当重新定义一个函数的时候,我们需要在新的实现中调用原来的那个函数

《Lua程序设计》--学习4

 上述代码使用了do代码段来限制局部变量oldSin的作用范围;根据可见性规则,局部变量oldSin只在这部分代码段中有效。因此,只有新版本的函数sin才能访问原来的sin函数,其他部分的代码则访问不了。

我们可以使用同样的技巧来创建安全的运行时环境(secure environment),即所谓的沙盒(sandbox)。

我们可以通过使用闭包重定义函数io.open来限制一个程序能够访问的文件:

 《Lua程序设计》--学习4

模式匹配

模式匹配的相关函数

函数string.find

函数string.find用于在指定的目标字符串中搜索指定的模式

函数string.find找到一个模式后,会返回两个值:匹配到模式开始位置的索引和结束位置的索引。如果没有找到任何匹配,则返回nil:

《Lua程序设计》--学习4

匹配成功后,可以以函数find返回的结果为参数调用函数string.sub来获取目标字符串中匹配相应模式的子串。对于简单的模式来说,这一般就是模式本身。

函数string.find具有两个可选参数。第3个参数是一个索引,用于说明从目标字符串的哪个位置开始搜索。第4个参数是一个布尔值,用于说明是否进行简单搜索(plain search)。字如其名,所谓简单搜索就是忽略模式而在目标字符串中进行单纯的“查找子字符串”的动作:

请注意,如果没有第3个参数,是不能传入第4个可选参数的。

函数string.match

函数string.match返回的是目标字符串中与模式相匹配的那部分子串,而非该模式所在的位置:

《Lua程序设计》--学习4

《Lua程序设计》--学习4

 函数string.gsub

函数string.gsub有3个必选参数:目标字符串、模式和替换字符串(replacementstring),其基本用法是将目标字符串中所有出现模式的地方换成替换字符串

《Lua程序设计》--学习4

 此外,该函数还有一个可选的第4个参数,用于限制替换的次数:

《Lua程序设计》--学习4

 除了替换字符串以外,string.gsub的第3个参数也可以是一个函数或一个表,这个函数或表会被调用

函数string.gsub还会返回第2个结果,即发生替换的次数

函数string.gmatch

函数string.gmatch返回一个函数,通过返回的函数可以遍历一个字符串中所有出现的指定模式。例如,以下示例可以找出指定字符串s中出现的所有单词

《Lua程序设计》--学习4

 模式'%a+'会匹配一个或多个字母组成的序列(也就是单词)。因此,for循环会遍历所有目标字符串中的单词,然后把它们保存到列表words中

模式

Lua语言的解决方案更加简单:Lua语言中的模式使用百分号(percent sign)作为转义符

总体上,所有被转义的字母都具有某些特殊含义(例如'%a'匹配所有字母),而所有被转义的非字母则代表其本身(例如'%.'匹配一个点)。

所谓字符分类,就是模式中能够与一个特定集合中的任意字符相匹配的一项。例如,分类%d匹配的是任意数字。因此,可以使用模式'%d%d/%d%d/%d%d%d%d'来匹配dd/mm/yyyy格式的日期

《Lua程序设计》--学习4《Lua程序设计》--学习4

 这些类的大写形式表示类的补集。例如,'%A'代表任意非字母的字符

《Lua程序设计》--学习4

在输出函数gsub的返回结果时,我们使用了额外的括号来丢弃第二个结果,也就是替换发生的次数

当在模式中使用时,还有一些被称为魔法字符(magic character)的字符具有特殊含义。Lua语言的模式所使用的魔法字符包括:

《Lua程序设计》--学习4

 百分号同样可以用于这些魔法字符的转义。因此,'%?'匹配一个问号,'%%'匹配一个百分号

可以使用字符集(char-set)来创建自定义的字符分类,只需要在方括号内将单个字符和字符分类组合起来即可。例如,字符集'[%w_]'匹配所有以下画线结尾的字母和数字,'[01]'匹配二进制数字,'[%[%]]'匹配方括号

还可以在字符集中包含一段字符范围,做法是写出字符范围的第一个字符和最后一个字符并用横线将它们连接在一起。例如,'%d'相当于'[0-9]','%x'相当于'[0-9a-fA-F]'。不过,如果需要查找一个八进制的数字,那么使用'[0-7]'就比显式地枚举'[01234567]'强多了。

在字符集前加一个补字符^就可以得到这个字符集对应的补集:模式'[^0-7]'代表所有八进制数字以外的字符,模式'[^\n]'则代表除换行符以外的其他字符。当然,以大写形式也可以获得补集

还可以通过描述模式中重复和可选部分的修饰符(modifier,在其他语言中也被译为限定符)来让模式更加有用。Lua语言中的模式提供了4种修饰符:

《Lua程序设计》--学习4

 修饰符+匹配原始字符分类中的一个或多个字符,它总是获取与模式相匹配的最长序列。例如,模式'%a+'代表一个或多个字母(即一个单词

《Lua程序设计》--学习4

捕获

捕获(capture)机制允许根据一个模式从目标字符串中抽出与该模式匹配的内容来用于后续用途,可以通过把模式中需要捕获的部分放到一对圆括号内来指定捕获。

对于具有捕获的模式,函数string.match会将所有捕获到的值作为单独的结果返回;换句话说,该函数会将字符串切分成多个被捕获的部分

《Lua程序设计》--学习4

模式'%a+'表示一个非空的字母序列,模式'%s*'表示一个可能为空的空白序列

模式中的两个字母序列被分别放在圆括号中,因此在匹配时就能捕获到它们

《Lua程序设计》--学习4

《Lua程序设计》--学习4

 它所匹配的内容依次是:一个左方括号、零个或多个等号、另一个左方括号、任意内容(即字符串的内容)、一个右方括号、相同数量的等号及另一个右方括号

《Lua程序设计》--学习4

 第1个捕获是等号序列(在本例中只有一个),第2个捕获是字符串内容

《Lua程序设计》--学习4

替换

函数string.gsub的第3个参数不仅可以是字符串,还可以是一个函数或表。当第3个参数是一个函数时,函数string.gsub会在每次找到匹配时调用该函数,参数是捕获到的内容而返回值则被作为替换字符串。当第3个参数是一个表时,函数string.gsub会把第一个捕获到的内容作为键,然后将表中对应该键的值作为替换字符串。如果函数的返回值为nil或表中不包含这个键或表中键的对应值为nil,那么函数gsub不改变这个匹配

先举一个例子,下述函数用于变量展开(variable expansion),它会把字符串中所有出现的$varname替换为全局变量varname的值:

《Lua程序设计》--学习4

 _G是预先定义的包括所有全局变量的表

URL编码

我们的下一个示例中将用到URL编码,也就是HTTP所使用的在URL中传递参数的编码方式。这种编码方式会将特殊字符(例如=、&和+)编码为"%xx"的形式,其中xx是对应字符的十六进制值。此外,URL编码还会将空格转换为加号。例如,字符串"a+b=c"的URL编码为"a%2Bb+%3D+c"。最后,URL编码会将每对参数名及其值用等号连接起来,然后将每对name=value用&连接起来。例如,值

《Lua程序设计》--学习4

 对应的URL编码为"name=al&query=a%2Bb+%3D+c&q=yes+or+no"。

现在,假设要将这个URL解码并将其中的键值对保存到一个表内,以相应的键作为索引,那么可以使用以下的函数完成基本的解码:

《Lua程序设计》--学习4

 第一个gsub函数将字符串中的所有加号替换为空格,第二个gsub函数则匹配所有以百分号开头的两位十六进制数,并对每处匹配调用一个匿名函数。这个匿名函数会将十六进制数转换成一个数字(以16为进制,使用函数tonumber)并返回其对应的字符(使用函数string.char)。

可以使用函数gmatch来对键值对name=value进行解码。由于键名和值都不能包含&或=,所以可以使用模式'[^&=]+'来匹配它们:

《Lua程序设计》--学习4

 调用函数gmatch会匹配所有格式为name=value的键值对。对于每组键值对,迭代器会返回对应的捕获(在匹配的字符串中被括号括起来了),捕获到的内容也就是name和value的值。循环体内只是简单地对两个字符串调用函数unescape,然后将结果保存到表cgi中

对应的编码函数也很容易编写。先写一个escape函数,用它将所有的特殊字符编码为百分号紧跟对应的十六进制形式(函数format的参数"%02X"用于格式化输出一个两位的十六进制数,若不足两位则以0补齐),然后把空格替换成加号:

《Lua程序设计》--学习4

 encode函数会遍历整个待编码的表,然后构造出最终的字符串

《Lua程序设计》--学习4

 制表符展开

《Lua程序设计》--学习4

 函数gsub会匹配字符串中所有的制表符并捕获它们的位置。对于每个制表符,匿名函数会根据其所在位置计算出需要多少个空格才能恰好凑够一列(整数个tab):该函数先将位置减去1以从0开始计数,然后加上corr凑整之前的制表符(每一个被展开的制表符都会影响后续制表符的位置)。之后,该函数更新下一个制表符的修正量:为正在被去掉的制表符减1,再加上要增加的空格数sp。最后,这个函数返回由替代制表符的合适数量的空格组成的字符串。

为了完整起见,让我们再看一下如何实现逆向操作,即将空格转换为制表符。第一种方法是通过空捕获来对位置进行操作,但还有一种更简单的方法:即在字符串中每隔8个字符插入一个标记,然后将前面有空格的标记替换为制表符。

《Lua程序设计》--学习4

 这个函数首先对字符串进行了制表符展开以移除其中所有的制表符,然后构造出一个用于匹配所有8个字符序列的辅助模式,再利用这个模式在每8个字符后添加一个标记(控制字符\1)。接着,它将所有以此标记结尾的空格序列都替换为制表符。最后,将剩下的标记删除(即那些没有位于空格后的标记)。文章来源地址https://www.toymoban.com/news/detail-476271.html

到了这里,关于《Lua程序设计》--学习4的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 《Lua程序设计第四版》 第二部分14~17章自做练习题答案

    Lua程序设计第四版第二部分编程实操自做练习题答案,带⭐为重点。 该函数用于两个稀疏矩阵相加 改写队列的实现,使得当队列为空时两个索引都返回0 修改图所用的数据结构,使得图可以保存每条边的标签。该数据结构应该使用包括两个字段的对象来表示每一条边,即边的

    2024年02月13日
    浏览(25)
  • 《Lua程序设计第四版》 第二部分9~13章自做练习题答案

    Lua程序设计第四版第二部分编程实操自做练习题答案,带⭐为重点。 请编写一个函数integral,该函数以一个函数f为参数并返回其积分的近似值 使用右矩阵法近似积分值 如下代码段将输出什么结果 编写练习5.4的柯里化版本 柯里化(Currying)是把接受 多个参数 的函数变换成

    2024年02月13日
    浏览(27)
  • 《Lua程序设计第四版》 第一部分前8章自做练习题答案

    Lua程序设计第四版第一部分语言基础自做练习题答案,带⭐为重点。 运行阶乘的示例并观察,如果输入负数,程序会出现什么问题?试着修改代码来解决问题 输入负数,程序会死循环,修改如下 分别使用-l参数和dofile运行twice示例,并感受你喜欢哪种方式 载入库,在 lua解释

    2024年02月13日
    浏览(37)
  • 【程序设计】函数式编程

    函数式编程(Functional Programming,FP)是一种编程范式,它强调使用纯函数(Pure Function)来构建程序。这些纯函数接受输入并返回输出,不改变系统状态或在执行过程中引入副作用(Side Effects),这使得函数式编程具有可预测性、可维护性和可测试性等优点。 以下是一些函数

    2023年04月24日
    浏览(73)
  • C++程序设计函数部分(定义+实例)

    目录 1、内联函数 2、默认形参值函数 3、重载函数 4、系统函数 (1)定义 在函数前面加上 inline 申明 eg: inline double CalArea(double radius) { return 3.14*radius*radius; } void main() { double r(3.0); double area; area=CalArea(r); coutareaendl; } (2)作用 提高运行的速度。 对于一些程序代码小,运行时间

    2023年04月14日
    浏览(30)
  • 《Python程序设计》 第六章 函数+编程题解

    目录 6-1 使用函数求特殊a串数列和 6-2 使用函数求素数和 6-3 使用函数统计指定数字的个数 6-4 使用函数输出指定范围内Fibonacci数的个数  6-5 使用函数求余弦函数的近似值 6-6 缩写词 7-1 输入列表,求列表元素和(eval输入应用) 7-2 一帮一 7-3 验证“哥德巴赫猜想” 7-4 列表或元组

    2024年02月07日
    浏览(55)
  • 【C++ 程序设计入门基础】- 第4节-函数

    函数是对实现某一功能的代码的模块化封装。  函数的定义: 标准函数: 输入 n 对整数的 a、b ,输出它们的和。 运行结果如下:  无返回值: 输入n,输出1~n之间所有整数。 运行结果:  无参数: 输入n,如果n为10的倍数,输出3个“very good!”。  运行结果如下:  传值参

    2024年01月17日
    浏览(29)
  • 【C语言】用函数实现模块化程序设计

    前言:如果把所有的程序代码都写在一个主函数( main函数 )中,就会使主函数变得庞杂、头绪不清,使阅读和维护程序变得困难。此外,有时程序中要多次实现某一功能,如果重新编写实现此功能就会使得程序冗长、不精炼。 💖 博主CSDN主页:卫卫卫的个人主页 💞 👉 专栏分

    2024年02月08日
    浏览(28)
  • 计算机程序设计-第4周(函数定义和调用)

    本关任务:定义一个函数isPrimeNumber,传入一个整数参数n,判断n是否为素数,返回判断结果。 然后编写主函数,对该函数功能进行测试,要求用户输入一个正整数,根据isPrimeNumber的返回值输出对应结果:是素数则输出“是素数”,不是素数则输出“不是素数”。

    2024年02月06日
    浏览(27)
  • Python 程序设计入门(018)—— format() 函数的用法详解

    format() 函数可以对数据进行格式化处理,将值转换为由 format_spec 控制的【格式化】表示形式。format() 函数的语法格式如下: 说明: (1)value:要转换的数据。 (2)format_spec:格式化解释,取决于值参数的类型; (3)默认情况下,format_spec 是一个空字符串,通常与调用 st

    2024年02月03日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包