Lua函数进阶

函数是值类型

《programming in lua》里面举了一个非常生动的例子:

a = {p = print} 
a.p("Hello World") --> Hello World 
print = math.sin -- `print' now refers to the sine function 
a.p(print(1)) --> 0.841470 
sin = a.p -- `sin' now refers to the print function 
sin(10, 20) --> 10 20

意味着当我们把函数变量指向另一个函数的时候,原地址依然保存着那份函数,可以用其他变量名指向这个函数,并接着调用。

高级函数

table.sort实际上就是一个典型的高级函数,其第二个参数即是function类型的,用于确定两个值哪个值应当在前:

table.sort(list,function(a,b)return a > b
end)

词法定界&闭包

这是我认为lua最灵活强大的功能之一。

词法定界的意思即是,局部函数内部可以访问局部函数外部的变量:

bAscending = true
a = {2,3,1,6,7,8}
table.sort(a,function(num1,num2)if bAscending thenreturn num1 < num2elsereturn num1 > num2end
end)
for i = 1,#a doprint(a[i])
end

如上,bAscending可以在比较函数外面被访问到。

官方文档中一个更有意思的示例代码,我修改了一点以更清晰:

function newCounter() local i = 0 local f = function() -- anonymous function i = i + 1return iendprint(f)return f
end
c1 = newCounter() --> function: 000001A8D24EB820
print(c1()) --> 1 
print(c1()) --> 2
c2 = newCounter() --> function: 000001A8D24EBAC0
print(c2()) --> 1 
print(c1()) --> 3 
print(c2()) --> 2

再略微修改上方的一些代码,观察输出:

function newCounter() local i = 0 local f = function() -- anonymous function i = i + 1return iendprint(f)return f
end
c1 = newCounter --> 
print(c1()) --> function: 000001D128558270 function: 000001D128558270
print(c1()) --> function: 000001D128558840 function: 000001D128558840
c2 = newCounter --> 
print(c2()) --> function: 000001D1285583F0 function: 000001D1285583F0
print(c1()) --> function: 000001D128558600 function: 000001D128558600
print(c2()) --> function: 000001D1285582A0 function: 000001D1285582A0

发现不一样的地方了吗?

有几点我们需要额外注意:

  1. 第一段代码的c1和c2最终其实都是闭包函数本身,在初始化的第一时间,就执行了print(f)的操作,自此之后,c1和c2就是f,所以print(c1())实际就是print(f())
  2. c1和c2初始化时的f相当于是开创的新变量,其指向的是全新的值,所以可以看到初始化时候的print(f)显示的地址是新地址,而且执行c1()和c2()的计数是完全独立,不受影响的
  3. 第二段代码的c1和c2实际是newCounter函数,所以第一时间没有输出值,而执行print(c1()) or print(c2())函数时print了两个完全相同的地址,其实也很好理解,相当于每次执行newCounter,其内部print了一次闭包函数地址,返回的又是此函数的地址,所以print了两次一样的值。
  4. 第二段代码每一次print出来的function地址都不一致,意味着每次执行这个函数,闭包函数地址都会变,进一步解释了第二条的计数完全独立的原因。

尾调用

很简单的一个概念,即return一个新函数时,不开辟额外栈空间去单独执行,而更类似于goto的状态转换。计算机基础里面有提到过,函数内部调用函数相当于开辟一个栈空间去执行,以此类推,直到一层层返回值即一层层清除栈空间,所以会有“调用函数会占空间”的这么一个说法。但是如下示例时:

function f(x) return g(x) 
end

lua解释器利用“调用g后不会再做任何事情,这种情况下当被调用函数g结束时程序不需要返回到调用者f”的特性,直接采用类似goto的做法跳转到g(x)执行,所以理论上尾调用递归的层次可以无限制的。但是需要分清什么是尾调用,只有return g(...)的情况才是,任何其他情况比如先g(...)再return,或者return g(...)+1这种都需要开辟额外栈空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/671319.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

符号绑定和函数绑定

符号绑定 其实我们平常定义的变量等&#xff0c;在编译之后都会被转化为地址&#xff0c;这些变量我们称之为符号。 我们在程序运行的时候&#xff0c;使用变量&#xff0c;其实就是从变量转换的地址中找到内存中存储的数据。符号绑定其实就是: 将我们程序中使用到的变量名等…

Leetcode 518 零钱兑换 II

题意理解&#xff1a; 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 将coins看作不同重量的背包&#xff0c;然后把要凑成的组…

unity实现第一人称和第三人称

在角色设置两个挂载点&#xff0c;第一人称时&#xff0c;相机放在eys上面&#xff0c;切换第三人称时&#xff0c;放置到3rd节点上面&#xff0c;调整节点位置&#xff0c;达到期望效果 代码 void ThirdView(){Debug.Log("切换到第三人称");camera.SetParent(third…

C# Task的使用

C#中的Task类是.NET框架中用于实现异步编程的核心组件之一&#xff0c;它在.NET Framework 4及更高版本以及.NET Core中广泛使用。Task对象代表一个异步操作&#xff0c;并提供了跟踪异步操作状态、获取结果和处理完成通知的方法。 Task 类提供了对异步操作的封装&#xff0c;…

SaperaCamExpert(相机专家)中文使用指南

参考&#xff1a;SaperaCamExpert中文使用指南.PDF 文章目录 软件介绍安装首次打开资源占用率功能主界面布局菜单栏FileViewPre-Processing&#xff1a;预处理 Tools&#xff1a; 快捷键&#xff1a;新建&#xff1b;打开&#xff1b;保存&#xff1b;帮助Device窗体属性树图像…

【MATLAB】使用梯度提升树在回归预测任务中进行特征选择(深度学习的数据集处理)

1.梯度提升树在神经网络的应用 使用梯度提升树进行特征选择的好处在于可以得到特征的重要性分数&#xff0c;从而识别出对目标变量预测最具影响力的特征。这有助于简化模型并提高其泛化能力&#xff0c;减少过拟合的风险&#xff0c;并且可以加快模型训练和推理速度。此外&…

莉莉与神奇花朵的冒险

现在&#xff0c;我将根据这些步骤编写一个对话形式的童话故事。 在很久很久以前的一个小村庄里&#xff0c;有一个勤劳善良的小女孩叫莉莉。她住在一间小茅屋里&#xff0c;和她的奶奶一起生活。奶奶年纪大了&#xff0c;行动不便&#xff0c;所以莉莉每天都要照顾她。 一天&a…

cpp智能指针篇(一):关于auto_ptr的一切

目录 写在前面 总览 智能指针的分类 为什么要使用智能指针&#xff1f; auto_ptr 致谢 写在前面 在过去几天中&#xff0c;我深入研究了智能指针的相关知识。这已经不是我第一次学习智能指针了&#xff0c;但这一次&#xff0c;我感觉自己真正理解了智能指针的工作原理。…

springboot war包部署 和jar包部署

文章目录 war包部署设置打包方式为war排除内嵌的tomcat在插件中指定入口类打包测试 jar包部署设置打包方式执行打包测试访问修改插件版本指定jsp打包配置 重新打包测试 war包部署 设置打包方式为war 执行项目打包的方式为 "war" 默认创建springboot项目打包都是ja…

vscode的ssh忽然连不上服务器:远程主机可能不符合glibc和libstdc++ VS Code服务器的先决条件

vscode自动更新了一下就发现连不上服务器了&#xff0c;我寻思估计一大堆人都寄了&#xff0c;一搜&#xff0c;果然哈哈哈哈 然后我直接搜一天内新发布的博客&#xff0c;还真给我搜到了这个问题&#xff0c;按照这个问题里面的回答&#xff08;vscode1.86无法远程连接waitin…

TCP和UDP相关问题(重点)(2)

2.TCP和UDP的使用场景&#xff1f; UDP一般使用在传输数据的准确性要求不是特别高的场景&#xff0c;传输速度块。例如&#xff1a;视频通话&#xff0c;直播&#xff0c;语音。 TCP一般使用在对数据传输准确度特别高的场景&#xff0c;虽然速度慢一些&#xff0c;但也要保证数…

2024.2.6日总结(小程序开发3)

页面配置 页面配置和全局配置的关系&#xff1a; 小程序中&#xff0c;app.json中的window节点&#xff0c;可以全局配置小程序中每个页面的窗口表现 如果某些小程序想要有特殊的窗口表现&#xff0c;可以用页面级别的.json配置文件实现这个需求 页面配置和全局配置冲突时&…

Apache Kafka: 强大消息队列系统的介绍与使用

目录 引言 一、什么是Apache Kafka&#xff1f; 二、核心概念&#xff1a; 三、架构与工作原理&#xff1a; 四、使用实例&#xff1a; 五、优势与挑战&#xff1a; 六、结论&#xff1a; 引言 随着互联网技术的飞速发展&#xff0c;分布式系统变得越来越复杂&#xff0…

SpringBoot之整合PageHelper分页插件

SpringBoot之整合PageHelper分页插件 文章目录 SpringBoot之整合PageHelper分页插件1. 引入坐标2. application.yml配置3. 基本使用4. 对多个查询执行分页1. 默认第一个Select语句会执行分页2. 让Pagehelper也能执行多个分页的方法3. 完整案例 详细配置请查看官网或MyBatis分页…

程序员为什么不喜欢关电脑?

目录 标题&#xff1a;程序员为何乐见电脑长时间处于关闭状态&#xff1f; 引言&#xff1a; 一、思维的延续性&#xff1a; 二、环境的连续性&#xff1a; 三、长时间开机的原因&#xff1a; 四、恢复成本的考量&#xff1a; 结论&#xff1a; 特别的&#xff1a; 不是…

c++ 子进程交互 逻辑

目录 一、主进程逻辑 1、创建子进程时候,写入自己的HWND 2、响应子进程消息

亿某通电子文档安全管理系统 UploadFileToCatalog SQL注入漏洞

免责声明&#xff1a;文章来源互联网收集整理&#xff0c;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;所产生的一切不良后果与文章作者无关。该…

leetcode | 杨辉三角 | 电话号码配对

电话号码的字母组合 class Solution {string _num[10] {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};public:void Combinations(const string& di…

Python __file__属性:查看模块的源文件路径

除可以查看模块的帮助信息之外&#xff0c;还可以直接阅读模块的源代码来掌握模块功能&#xff0c;提升 Python 编程能力。 不管学习哪种编程语言&#xff0c;认真阅读那些优秀的框架、库的源代码都是非常好的学习方法。 通过模块的 __file__ 属性即可查看到指定模块的源文件…

防范恶意勒索攻击!亚信安全发布《勒索家族和勒索事件监控报告》

本周态势快速感知 本周全球共监测到勒索事件81起&#xff0c;事件数量有所下降&#xff0c;比上月降低20%。 lockbit3.0仍然是影响最严重的勒索家族&#xff1b;akira和incransom也是两个活动频繁的恶意家族&#xff0c;需要注意防范。 本周alphv勒索组织窃取MBC法律专业公司…