命名参数 |
|
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)
sum(z=3*i, y=2*i, i)
作为 sum['z,y'](3*i, 2*i, i)
的语法糖,以便可以记忆命名参数解析。还探讨了命名参数对 LuaJit 优化的影响。