R包内使用devtools::load_all()时工厂函数输出赋值失败求助
我在回复中写了一个较为笨拙的解决方案,但不确定这是否是实现这类常见需求的正确方式。
我编写的函数都遵循同一模板:给定若干内部函数及其部分参数,一个「外层函数」对这些内部函数进行求和与乘积运算。外层函数的组合方式是预先设定的,但用户需要能自由设置内部函数的参数。由于无法预知具体函数及参数,创建一次性包装函数不可行。
为了代码的维护与优化,我认为创建一个「工厂函数」来生成所需的外层函数是合理的。该工厂函数既能帮助我为包创建其他实用函数,也允许用户生成同类型的自定义函数。我参考相关资料实现了该工厂函数,在常规R会话中加载并运行该工厂函数,将其输出赋值给变量后,生成的函数完全符合预期。
但在R包内使用devtools::load_all()执行相同的赋值操作时,却无法正常工作。
我进行了排查与实验,缩小了问题范围,相关实验及结果已放在公开仓库中。这是我首次接触函数组件、环境等正式概念,可能术语或理解存在偏差。下方是我整理的最小复现代码。
我认为这并非devtools或包配置问题。我查阅了一些旧帖与博客,包括相关讨论和文档。
geom_shifted.R中的dgeom_shifted_manual():为已知函数及参数创建包装函数无问题,这是一个展示部分预期行为的硬编码示例。geom_shifted.R中的dgeom_shifted_programmatic():该函数实现了我需要的行为。给定一个函数(后续会扩展到多个函数)及部分参数的转换规则,工厂函数shift_support()会输出我需要的外层函数。在交互R会话中,该函数完全符合预期:若注释掉它的创建代码,运行load_all()后再取消注释并执行创建,可得到预期结果;但如果不注释直接运行load_all(),会报错:
Error in
load_all():
Failed to load R/geom_shifted.R
Caused by error inshift_support():
could not find function "shift_support"
Runrlang::last_trace()to see where the error occurred.
此时R无法找到该工厂函数。
geom_shifted.R中的util_caller2():该示例表明,若将工具函数(即使未导出到namespace)包装在另一个函数内,可正常工作。geom_shifted.R中的util_caller3:直接将geom_shifted.R中的对象赋值为support_utils.R中定义的函数的输出,会出现相同的"could not find function"错误。geom_shifted.R中的stats_util_caller1:(同上)geom_shifted.R中的stats_util_caller2:即使工厂/工具函数与赋值代码在同一文件中,仍会出现相同问题。geom_shifted.R中的uses_stats_util_caller2:表明创建一个伪包装函数可避免报错,但无法真正解决问题。
总体来看,这似乎不是命名空间问题。我认为可通过修改工厂函数的调用环境来解决问题——这也是伪包装函数能避免报错的原因。但我对环境与包的相关知识不足,无法实现无额外操作的正常运行(无需创建单独的utils配套包)。
我还觉得,即便能按上述方式解决,这可能也不是规范的实现方式。我的需求在函数式编程中应该很常见,但我尚未找到针对R包场景的相关资料。从更宏观的角度看,是否存在更深层次的设计问题?我的思路是否有误?
非常感谢!
创建一个新包,在源文件中粘贴以下代码:
uses_stats_util_caller2 <- function() { stats_util_in_same_file(stats::dgeom) } ### 取消注释并运行load_all()会报错:"...could not find function..." ### 这只是权宜之计,并未真正解决问题 # uses_uses_stats_util_caller2 <- uses_stats_util_caller2() stats_util_in_same_file <- function(distribution_function) { return(distribution_function) }
- 在终端运行
load_all(),此时不会报错但也无实际功能。 - 接着在终端执行赋值语句
uses_uses_stats_util_caller2 <- uses_stats_util_caller2(),可得到预期结果。 - 现在取消注释上述赋值代码块,重新运行
load_all(),会出现报错。 - 若将
stats_util_in_same_file()放在单独的源文件并导出(更贴近真实场景),结果一致。
内容的提问来源于stack exchange,提问作者user23471877

