lua select,# 应用

前言

#运算符取table的长度,不论在lua5.1,还是在lua5.3都不能保证结果的正确性,在某些场景下,我们可以使用select来替代#得到正确的结果。

测试

测试代码1

local function test()
    return 1, nil, 3, 4, nil, 6, nil, nil, nil, 10, nil, 12, nil
end

local ret = {test()}

print(string.format("ret的长度为: %d", #ret))

输出

root@localhost:~/work/test/lua lua sel1.lua
ret的长度为: 6

测试代码2

local function test()
    return 1, nil, 3, 4, nil, 6, nil, nil, nil, 10, nil, 12, nil, 14
end

local ret = {test()}

print(string.format("ret的长度为: %d", #ret))

输出

root@localhost:~/work/test/lua lua sel1.lua
ret的长度为: 14

测试代码3

local function test()
    return 1, nil, 3, 4, nil, 6, nil, nil, nil
end

local ret = {test()}

print(string.format("ret的长度为: %d", #ret))

输出

root@localhost:~/work/test/lua lua sel1.lua
ret的长度为: 6

测试代码4

local function test()
    return 1, nil, 3, 4, nil, 6, nil, nil, nil, 10
end

local ret = {test()}

print(string.format("ret的长度为: %d", #ret))

输出

root@localhost:~/work/test/lua lua sel1.lua
ret的长度为: 10

测试代码5

local function test()
    return 1, nil, 3, 4, nil, 6, nil, nil, nil, nil
end

local ret = {test()}

print(string.format("ret的长度为: %d", #ret))

输出

root@localhost:~/work/test/lua lua sel1.lua
ret的长度为: 1

测试结果

  1. 实际长度:13, 最后一位:nil,结果:6 错误
  2. 实际长度:14, 最后一位:不为nil,结果:14 正确
  3. 实际长度:9, 最后一位:nil,结果:6 错误
  4. 实际长度:10, 最后一位:不为nil,结果:10 正确
  5. 实际长度:10, 最后一位:nil,结果:1 错误

分析

以上测试结果在 lua5.1 和 lua5.3 环境均相同,官方手册也有说明,#table 必须是连续序列才能保证返回值的正确性。以前看过 # 取 table 长度在 lua 源码中的实现,还留有一点印象,再加上这个测试结果可以分析下 #table 的计算流程:用二分查找查找到尽可能大的值。流程图如下:
lua select,# 应用_第1张图片

select介绍

select (index, ···)

如果 index 是个数字, 那么返回参数中第 index 个之后的部分; 负的数字会从后向前索引(-1 指最后一个参数)。 否则,index 必须是字符串 “#”, 此时 select 返回参数的个数。 <>

select使用示例

场景1:获取返回值数量

local function test()
    return 1, nil, 3, 4, nil, 6, nil, nil, nil, 10, nil, 12, nil
end

print(select("#", test()))

正确返回了实际个数

root@localhost:~/work/test/lua lua sel2.lua
13

场景2:获取第n个返回值以后的数据

local function test()
    return 1, nil, 3, 4, nil, 6, nil, nil, nil, 10, nil, 11, nil
end

print(select("#", test()))
print(select(4, test()))

输出了从第4个索引开始的所有返回值,包括nil

root@localhost:~/work/test/lua lua sel2.lua
13
4       nil     6       nil     nil     nil     10      nil     11      nil

场景3:获取变长参数的长度

function test(str, ...)
    print(select("#", ...))
end

test(1, 1)
test(1, 1, 2)
test(1, 1, 2, 3)
test(1, nil, 2, 3)
test(1, nil, 2, nil)
test(1, nil, nil, nil)

都输出了正确的参数个数

root@localhost:~/work/test/lua lua select.lua
1
2
3
3
3
3

我们可以基于场景3 重写一下 print 方法:

function my_print(str, ...)
    local len = select("#", ...)
    str = len == 0 and str or string.format(str, ...)
    print(str)
end

my_print("no val")
my_print("val1: %s, val2: %s, val3: %s", 1, 2, 3)
my_print("val1: %s, val2: %s, val3: %s", nil, 2, 3)
my_print("val1: %s, val2: %s, val3: %s", nil, 2, nil)
my_print("val1: %s, val2: %s, val3: %s", nil, nil, nil)

以下为 lua5.3 环境下的输出,lua5.1 还不支持 format 参数中非 string 类型自动转换为 string 类型,需要自己保证 format 的格式正确

root@localhost:~/work/test/lua lua53 select.lua
1
2
3
3
3
3
no val
val1: 1, val2: 2, val3: 3
val1: nil, val2: 2, val3: 3
val1: nil, val2: 2, val3: nil
val1: nil, val2: nil, val3: nil

结语

select具体实现我没有去深入了解,可能会有性能方面的损耗,所以如果是在调度频繁的逻辑中使用的话需要慎重一点,可以看下源码实现或者自己测试一下。

你可能感兴趣的:(lua,lua,开发语言,c语言)