命名参数

lua-users home
wiki

Lua 通过将一个表传递给函数来支持命名参数。PiL 第 5 章描述了这一点 [1]。因此,以下函数和使用位置参数的调用

local function pipe(input, output)
  output:write(input:read'*a')
end
pipe(io.stdin, io.stdout)

可以以命名参数样式编写为

local function pipe(t)
  local input  = assert(t.input)
  local output = assert(t.output)
  output:write(input:read'*a')
end
pipe({input=io.stdin, output=io.stdout})
pipe{input=io.stdin, output=io.stdout}  -- the following shorter syntax is equivalent and preferrred

可选地,会执行某种类型的参数检查,最基本的形式涉及上面的 assert,这可能会变得更复杂,例如 结构化模式匹配。函数可能同时支持命名和位置调用约定

local function pipe(...)
  local input, output
  if type(...) == 'table' then
    local t = ...
    input, output = assert(t.input or t[1]), assert(t.output or t[2])
  else
    input, output = ...
  end
  output:write(input:read'*a')
end
pipe(io.stdin, io.stdout)               -- positional form
pipe{input=io.stdin, output=io.stdout}  -- named form
pipe{io.stdin, output=io.stdout}        -- mixed form (positional and named)
pipe{io.stdin, io.stdout}               -- mixed form (using only positional)

可选/默认参数也可以通过以下方式支持

input, output = assert(t.input or t[1] or io.stdin), assert(t.output or t[2] or io.stdout)

为了避免歧义,以上假设第一个位置参数永远不会是表,或者至少如果它是表,那么它可以与命名参数表区分开来。

程序员必须记住在使用命名或混合参数形式时将括号从 '()' 更改为 '{}'。

命名形式的一个缺点是,由于表分配,表构造确实会产生一定程度的开销。基准测试可以确定这在实践中有多重要。在紧密的计算密集型循环中,这可能是一个问题。

命名形式中的错误处理也稍微复杂一些,代码也更冗长/更难看,但如果位置参数具有必须检查的嵌套结构,则会发生相同的问题。

命名形式不保留尾随 nil,因为尾随 nil 在表构造函数中被忽略。因此,以下调用是无法区分的

f{a,b,nil,d=d}
f{a,b,d=d}

以下是一些其他不太传统的实现命名参数的可能方法

f(a, b, {c=c})
f('a',a, 'b',b, 'c',c)  -- note: Perl supports a syntactic sugar for this: `a=>$a` is `'a',$a`
f(params().a(a).b(b).c(c)) -- C++ chained method call style.
f('c,b,a', c,b,a)  -- note: parameter resolution can be memoized (see link below)
f'c=$(c),b=$(b),a=$(a)' -- variant of above with StringInterpolation [1] syntax sugar for f('c=$(c),b=$(b),a=$(a)',c,b,c)

另请参阅


最近更改 · 偏好设置
编辑 · 历史记录
最后编辑于 2012 年 3 月 13 日凌晨 4:17 GMT (差异)