R语言函数匹配方案对比:直接传参、match.fun()与deparse(substitute())
结论优先
三种实现里最推荐使用fun2()采用的match.fun()方案,fun1()直接调用的写法次之,fun3()的deparse()+do.call()方案完全不推荐。
为什么首先排除fun3()的实现
- 兼容性极差:如果传入匿名函数会直接报错,比如
fun3(function(x) sum(x^2), 1:10)完全无法运行,deparse(substitute(fun))对匿名函数的解析结果是无效的函数名 - 性能损耗大:涉及语言对象转字符串、再通过
do.call解析执行的过程,额外开销远高于另外两种实现 - 作用域风险高:字符串解析会受运行时环境的变量干扰,很容易出现意料之外的作用域问题
即使不需要支持字符串传参,match.fun()相比fun1()的直接调用也有很多不可替代的优势
报错更清晰,调试成本低
如果不小心传入非函数对象,fun1()的报错是通用的attempt to apply non-function,很难快速定位是fun参数出了问题;而match.fun()会直接返回明确的报错:argument "fun" is not a function, character or symbol,直接定位参数问题,调试效率高很多。避免内部同名变量的冲突坑
R的词法作用域很容易出现同名变量覆盖的问题,举个简单的例子:
# fun1的写法会被内部同名变量干扰 fun1 <- function(fun, x) { fun <- 100 # 不小心写了和参数同名的临时变量 fun(x) } fun1(mean, 1:10) # 直接报错非函数 # match.fun的写法不受内部同名变量影响 fun2 <- function(fun, x) { fun <- 100 fun <- match.fun(fun) fun(x) } fun2(mean, 1:10) # 正常返回5.5
哪怕是内部函数,开发过程中也难免会出现临时变量和参数重名的情况,match.fun()可以完全规避这类低级错误。
- 符合R的官方开发规范
R base包中所有接收函数作为参数的基础函数(比如lapply、apply、tapply等),底层都是用match.fun()做函数参数校验,是R社区公认的最佳实践,代码的可读性和可维护性更高。
而且match.fun本身是原生实现,性能损耗几乎可以忽略,额外加这一行代码的成本极低,收益却很高。
内容的提问来源于stack exchange,提问作者Thomas
相关产品推荐
相关产品推荐

