当前位置: 首页 > news >正文

Lua中关于table对象引用传递的注意事项

内容导航

  • 前言
  • 代码测试
  • 总结
  • 测试源码

前言

最近写了挺长一段时间的Lua,发现Lua这个语言真的是很随意,产生这种感觉的根本原因应该是它把“函数” 作为了“第一类值”,也就是说函数也可以作为变量的“值”,这使得Lua可以随处定义函数,进而改变逻辑的走向,整个流程任你摆布。

虽说把一个函数来回设置方便了许多,但是同样带来了一些不容易发现的问题,如果搞不清定义域和引用关系,常常会导致程序错误,比如最近用Lua写按钮的触发事件时,使用公有函数创建了对应的闭包,一开始感觉table的引用有问题,写了很多中转的代码,最后发现直接就可以使用,浪费了不少时间,最后仔细分析就是闭包最根本的形式,只不过被业务逻辑给干扰了视线,接下来我们一起看看,table和闭包究竟会发生什么关系!

代码测试

  1. table作为函数参数时的操作
print("\nexample 1:");
data_table = {a = 1, b = 2, 3, 4, 5, 6};
function filter(data_tb)
    for k,v in pairs(data_tb) do
        if v % 2 == 0 then
            data_tb[k] = nil;
        end
    end
end

-- 过滤掉偶数
filter(data_table);
for k,v in pairs(data_table) do
    print(k,v)
end
example 1:
1   3
3   5
a   1

以上为去掉table中的偶数的代码,直接操作参数data_tb就可以对传入的data_table进行改变,这样的逻辑一般不会出错,接着我们看下接下来需求,直接将表中数据清空。

print("\nexample 2:");
data_table = {a = 1, b = 2, 3, 4, 5, 6};
function destroy(data_tb)
    data_tb = {};
end

-- 销毁整个表
destroy(data_table);
for k,v in pairs(data_table) do
    print(k,v)
end
example 2:
1   3
2   4
3   5
4   6
b   2
a   1

看到这次的输出可能有些人就感到奇怪了,怎么上个例子改变元素可以,而这里直接给变量data_tb赋值,变成空表怎么不行了?这是因为data_tb是对变量data_table的整体引用,所以可以通过data_tb来改变变量data_table内部的值,但是当执行data_tb = {};代码时表示data_tb不再引用data_table,而去引用{}了,也就是data_tbdata_table脱离了关系,这一点可以类比C++代码:

#include <iostream>
using namespace std;

void change_string(char* pStr)
{
    pStr[0] = '5';
    pStr[1] = '0';

    pStr = "only test\n";
}

int main()
{
    char szContent[32] = "help";
    
    change_string(szContent);
    cout << szContent << endl;

    return 0;
}

分析一下这段代码的输出结果,如果你能知道结果为50lp,那说明你的C++水平已经超过了入门级别,理解了这段代码有助于清楚的理解前两段Lua代码。

  1. 看一个标准闭包实现的计数器
print("\nexample 3:");
function counter()
    local count = 0;
    return function()
        count = count + 1;
        return count;
    end
end

func = counter();
print(func());
print(func());
print(func());
example 3:
1
2
3

这段代码的不同之处就在于变量count,这是一个标准的计数器,也是一个标准的闭包,也就是说Lua支持这样的语法,闭包中可以在定义之后一直引用外部的变量,并且在返回函数的整个使用生命周期内都可以引用这个变量,加入外部修改了这个变量,闭包中引用的值也会改变,换句话来说就是闭包这种引用是引用的变量,而不是仅仅保存了一个值。

  1. lua中常见的table引用
print("\nexample 4:");
local t1 = {i = 1};
local t2 = t1;
t1.i = 666;
print(t2.i)
example 4:
666

这个例子类似于前面“过滤掉偶数”的代码,首先定义了表t1,然后定义了变量t2引用了变量t1,实际上这里t2不是定义了变量t1本身,而是引用了t1的值,也就是引用的是{i=1},这里通过t1.i = 666也可以影响到变量t2,其实这个例子看不出引用的究竟是变量t1还是t1的值,可以接着看下面的例子。

print("\nexample 5:");
local t1 = {i = 1};
local t2 = t1;
t1 = {i = 11};
print(t2.i)
example 5:
1

通过这个例子就很清楚了,前面的部分和上个例子一致,但是后面直接给变量t1赋值时并没有改变t2的值,由此可以看出t1t2已经“分离”了。

  1. table引用和闭包结合的例子
print("\nexample 6:");
local tb = {i= 1};

function outer()
    return function()
        local t = tb;
        print(t.i);
    end
end

local show = outer();
tb = {i = 6};
show();
example 6:
6

这个例子应该会有猜错结果的人,我自己就是在类似的代码中搞糊涂的,一种想法是函数outer定义的时候变量t的值已经定义了,还有一种就是认为在返回函数show的时候变量t的值会定义,但是这两种想法都是错误的,实际上是调用函数show的时候才给t赋值,这时变量t引用的是拥有最新值的tb,所以t.i的值是6,如果你猜对了这个例子的结果,接下来看看下面的代码。

print("\nexample 7:");
local tb = {i= 1};

function outer()
    local t = tb;
    return function()
        print(t.i);
    end
end

local show = outer();
tb = {i = 7};
show();
example 7:
1

如果清楚了上个例子的运行过程,就应该很容易知道这个例子的结果,其中变量t的值是在调用函数outer时确定的,所以后面的赋值tb = {i = 7};对变量t的值没有影响。

总结

  1. lua中操作变量注意值和引用,其实很多语言都有这种区分。
  2. 注意闭包可以访问外部变量的特性,程序中使用起来非常方便。
  3. 实际使用过程中往往还夹杂着业务逻辑,要学会挖掘本质问题,这样往往可以看到真正的运行逻辑。

测试源码

示例传送门:lua中table引用

相关文章:

  • VS2015调试dump文件时提示打不开KERNELBASE.dll
  • Mysql中使用select into语句给变量赋值没有匹配记录时的结果
  • 排序算法系列之(四)——抓扑克牌风格的插入排序
  • linux环境下服务器程序的查看与gdb调试
  • linux环境下运行程序常用的nohup和的区别
  • 排序算法系列之(五)——为目标打好基础的希尔排序
  • linux环境下查找包含指定内容的文件及其所在行数
  • Mysql查询可通过给条件字段添加索引提高查询速度
  • Mysql开启、查看慢查询日志
  • IP地址常见分类:A类、B类、C类、D类、E类
  • Mysql表连接:内连接、外连接、交叉连接、自然连接真的都不一样吗
  • C/C++版本更迭历程
  • gcc编译生成可执行文件的过程中发生了什么
  • Mysql中explain命令简析
  • Python利用requests模块实现代理访问网络
  • [译]如何构建服务器端web组件,为何要构建?
  • Angular 响应式表单之下拉框
  • echarts花样作死的坑
  • Hexo+码云+git快速搭建免费的静态Blog
  • Java到底能干嘛?
  • jquery ajax学习笔记
  • JS学习笔记——闭包
  • Linux链接文件
  • python学习笔记 - ThreadLocal
  • scrapy学习之路4(itemloder的使用)
  • vuex 学习笔记 01
  • 模仿 Go Sort 排序接口实现的自定义排序
  • 实战|智能家居行业移动应用性能分析
  • 视频flv转mp4最快的几种方法(就是不用格式工厂)
  • 在Unity中实现一个简单的消息管理器
  • ​LeetCode解法汇总2670. 找出不同元素数目差数组
  • ​渐进式Web应用PWA的未来
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • %@ page import=%的用法
  • (11)工业界推荐系统-小红书推荐场景及内部实践【粗排三塔模型】
  • (第8天)保姆级 PL/SQL Developer 安装与配置
  • (附源码)spring boot建达集团公司平台 毕业设计 141538
  • (蓝桥杯每日一题)love
  • *** 2003
  • .mysql secret在哪_MYSQL基本操作(上)
  • .NET中使用Redis (二)
  • /etc/fstab和/etc/mtab的区别
  • @SuppressWarnings注解
  • @Transaction注解失效的几种场景(附有示例代码)
  • @Validated和@Valid校验参数区别
  • [ 隧道技术 ] 反弹shell的集中常见方式(二)bash反弹shell
  • [<MySQL优化总结>]
  • [Android]创建TabBar
  • [Angular] 笔记 21:@ViewChild
  • [C# WPF] 如何给控件添加边框(Border)?
  • [CTO札记]如何测试用户接受度?
  • [Django 0-1] Core.Checks 模块
  • [DM复习]Apriori算法-国会投票记录关联规则挖掘(上)
  • [Flex] PopUpButton系列 —— 控制弹出菜单的透明度、可用、可选择状态
  • [Hadoop in China 2011] Hadoop之上 中国移动“大云”系统解析