可变长参数函数

简介: 可变长参数函数

Lua 语言中的函数可以是可变长参数(variadic),即可以支持数量可变的参数。例如: print 函数,虽然 print 函数是 C语言 中定义的,但也可以在 Lua 语言中定义可变长参数函数。如下所示:

function add(...)
    local s = 0
    for _, v in ipairs {...} do s = s + v end
    return s
end
print(add(1, 2, 3, 4, 5, 6))      --> 21


参数列表中的三个点... )表示该函数的参数是可变长的。当这个函数被调用时, Lua 内部会把它的所有参数收集起来,我们把这些被收集起来的参数称为函数的额外参数(extra argument)。当函数要访问这些参数时扔需要用到三个点,但不同的是此时这三个点是作为一个表达式来使用的。在上例中,表达式 {...} 的结果是一个由可变长参数组成的列表,该函数会遍历该列表来累加其中的元素。


我们将三个点组成的表达式称为可变长参数表达式,其行为类似于一个具有多个返回值的函数,返回的是当前函数的所有可变长参数。例如: print(...) 会打印出该函数的所有参数。


如下的代码创建了两个局部变量,其值为前两个可选的参数(如果参数不存在则为 nil ):

local a, b = ...


实际上,可以通过变长参数来模拟 Lua 语言中普通的参数传递机制,例如:

function foo (a, b, c)


可以写成:

function foo (...)
  local a, b, c = ...


形如下例的函数只是将调用它时所传入的所有参数简单地返回:

function id (...) return ... end


该函数是一个多值恒等式函数(multi-value identity function)。


下列函数的行为则类似于直接调用函数 foo ,唯一不同之处是在调用函数 foo 之前会先打印出传递给函数 foo 的所有参数:

function foo1 (...)
  print ("calling foo:",...)
  return foo(...)
end


跟踪对某个特定的函数调用时,这个技巧很有用。


Lua 语言提供了专门用于格式化输出的函数 string.format 和输出文本的函数 io.write 。我们会很自然地想到把这两个函数合并为一个具有可变长参数的函数:

function fwrite (fmt, ...)
  return io.write(string.format(fmt, ...))
end


注意,在三个点之前有一个固定的参数 fmt 。具有可变长参数的函数也可以具有任意数量的固定参数,但固定参数必须放在变长参数之前。 Lua 语言会先将前面的参数赋给固定参数,然后将剩余的参数(如果有)作为可变长参数。


遍历可变长参数

{...}

可以使用表达式 {...} 将可变长参数放在一个表中。不过,在某些罕见的情况下,如果可变长参数中包含无效的 nil ,那么 {...} 获得的表可能不再是一个有效的序列。此时,就没办法在表中判断原始参数究竟是不是以 nil 结尾。对于这种情况, Lua 提供了函数 table.pack 。该函数像表达式 {...} 一样保存所有的参数,然后将其放在一个表中返回,但是这个表还有一个保存了参数个数的额外字段 "n"


下面的函数使用了函数 table.pack 来检查参数中是否有 nil

function nonils (...)
  local arg = table.pack(...)
  for i = 1, arg.n do
    if arg[i] == nil then return false end
  end
  return true
end
print(nonils(1,2,nil))    --> false
print(nonils(2,3))        --> true
print(nonils())           --> true
print(nonils(nil))        --> false


select 函数

函数 select 总是具有一个固定的参数 selector ,以及数量可变的参数。如果 selector 是数值 n ,那么函数 select 则返回第 n 个参数后的所有参数;否则, selector 应该是字符串 "#" ,以便函数 select 返回额外参数的总数

print(select(1, "a", "b", "c"))       --> a   b   c
print(select(2, "a", "b", "c"))       --> b   c
print(select(3, "a", "b", "c"))       --> c
print(select("#", "a", "b", "c"))     --> 3


通常,我们在需要把返回值个数调整为 1 的地方使用函数 select ,因此可以把 select(n, ...) 认为是返回第 n 个额外参数的表达式。

function add (...)
  local s = 0
  for i = 1, select("#", ...) do
    s = s + select(i, ...)
  end
  return s
end


对于参数较少的情况,第二个版本的 add 更快,因为该版本避免了每次调用时创建一个新表。不过,对于参数较多的情况,多次带有很多参数调用函数 select 会超过创建表的开销,因此第一个版本会更好。


提示

由于迭代的次数和每次迭代时传入函数的个数会随着参数的个数增长,因此第二个版本的时间开销是二次代价的。

目录
相关文章
|
2月前
|
存储 编译器 C++
13函数
13函数
12 0
|
9月前
|
算法 编译器 程序员
|
8月前
MQTTClient_setCallbacks()函数
MQTTClient_setCallbacks()
472 0
|
9月前
|
前端开发
纯函数
纯函数
52 0
|
9月前
|
监控 程序员 C语言
|
10月前
|
程序员 C语言
函数(1)
函数(1)
|
10月前
|
程序员 C语言 C++
函函函函函函函函函函函数——one
函函函函函函函函函函函数——one
57 0
|
11月前
实现strcta函数
实现strcta函数
|
算法 编译器 C语言
函数部分的详细讲解
函数部分的详细讲解
|
算法 编译器
函数(二)
函数(二)
58 0
函数(二)