You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何传递分派方法的调用并结合默认值(R语言)

问题描述

我有一个泛型函数foo,包含默认方法和公式方法,需要实现以下需求:

  • 调用默认方法foo.default时,将调用信息作为attr属性返回
  • 调用公式方法foo.formula时,属性中的调用信息要保留原公式参数fo和data,而非内部生成的X和y
  • 无论用户是否显式指定,foo.default中的默认参数(bar=FALSE、method='1A'、beta=2)都要在返回的调用信息中体现

当前代码无法正确传递公式方法的调用信息到默认方法,且返回的调用存在多余的.cl参数和... = , NULL的问题。

当前代码

foo <- function(x, ...) UseMethod('foo')

foo.formula <- function(fo, data, ...) {
  .cl <- match.call()
  y <- model.response(model.frame(fo, data))
  X <- model.matrix(fo, data)
  foo.default(X, y, .cl=.cl)
}

foo.default <- function(X, y, bar=FALSE, method='1A', beta=2, ...) {
  if (!exists('.cl')) .cl <- match.call()
  fa <- formalArgs(foo.default)
  m <- match(names(.cl), fa, nomatch=0)
  .cl <- c(as.list(.cl), as.list(args(foo.default))[-m])
  .cl[[1]] <- as.name('foo')
  `attr<-`(lm.fit(X, y)$coefficients, 'call', as.call(.cl))
}

# 测试调用
foo(X1, y1)
# (Intercept)          hp 
# 30.09886054 -0.06822828 
# attr("call")
# foo(X = X1, y = y1, bar = FALSE, method = "1A", beta = 2, 
#             ... = , NULL)

foo(mpg ~ hp, mtcars)
# (Intercept)          hp 
# 30.09886054 -0.06822828 
# attr("call")
# foo(X = X, y = y, .cl = .cl, bar = FALSE, method = "1A", 
#             beta = 2, ... = , NULL)

期望输出

foo(X1, y1)
# (Intercept)          hp 
# 30.09886054 -0.06822828 
# attr("call")
# foo(X = X1, y = y1, bar = FALSE, method = "1A", beta = 2, ...)

foo(mpg ~ hp, mtcars)
# (Intercept)          hp 
# 30.09886054 -0.06822828 
# attr("call")
# foo(fo = mpg ~ hp, data = mtcars, bar = FALSE, method = '1A', beta = 2, ...)

测试数据

y1 <- c(`Mazda RX4` = 21, `Mazda RX4 Wag` = 21, `Datsun 710` = 22.8, 
`Hornet 4 Drive` = 21.4, `Hornet Sportabout` = 18.7, Valiant = 18.1, 
`Duster 360` = 14.3, `Merc 240D` = 24.4, `Merc 230` = 22.8, `Merc 280` = 19.2, 
`Merc 280C` = 17.8, `Merc 450SE` = 16.4, `Merc 450SL` = 17.3, 
`Merc 450SLC` = 15.2, `Cadillac Fleetwood` = 10.4, `Lincoln Continental` = 10.4, 
`Chrysler Imperial` = 14.7, `Fiat 128` = 32.4, `Honda Civic` = 30.4, 
`Toyota Corolla` = 33.9, `Toyota Corona` = 21.5, `Dodge Challenger` = 15.5, 
`AMC Javelin` = 15.2, `Camaro Z28` = 13.3, `Pontiac Firebird` = 19.2, 
`Fiat X1-9` = 27.3, `Porsche 914-2` = 26, `Lotus Europa` = 30.4, 
`Ford Pantera L` = 15.8, `Ferrari Dino` = 19.7, `Maserati Bora` = 15, 
`Volvo 142E` = 21.4)

X1 <- structure(c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 110, 110, 93, 110, 
175, 105, 245, 62, 95, 123, 123, 180, 180, 180, 205, 215, 230, 
66, 52, 65, 97, 150, 150, 245, 175, 66, 91, 113, 264, 175, 335, 
109), dim = c(32L, 2L), dimnames = list(c("Mazda RX4", "Mazda RX4 Wag", 
"Datsun 710", "Hornet 4 Drive", "Hornet Sportabout", "Valiant", 
"Duster 360", "Merc 240D", "Merc 230", "Merc 280", "Merc 280C", 
"Merc 450SE", "Merc 450SL", "Merc 450SLC", "Cadillac Fleetwood", 
"Lincoln Continental", "Chrysler Imperial", "Fiat 128", "Honda Civic", 
"Toyota Corolla", "Toyota Corona", "Dodge Challenger", "AMC Javelin", 
"Camaro Z28", "Pontiac Firebird", "Fiat X1-9", "Porsche 914-2", 
"Lotus Europa", "Ford Pantera L", "Ferrari Dino", "Maserati Bora", 
"Volvo 142E"), c("(Intercept)", "hp")), assign = 0:1)
解决方案

核心修改点在于:

  1. 在foo.formula中直接处理原始调用,不将.cl作为参数传入foo.default
  2. 在两种方法中分别合并用户参数与默认参数,确保未指定的默认值都能体现
  3. 清理多余参数和无效的...表达式

修改后的代码:

foo <- function(x, ...) UseMethod('foo')

foo.formula <- function(fo, data, ...) {
  .cl <- match.call()
  y <- model.response(model.frame(fo, data))
  X <- model.matrix(fo, data)
  
  # 调用默认方法计算结果
  res <- foo.default(X, y, ...)
  
  # 合并原始调用与默认参数
  default_args <- formals(foo.default)[!names(formals(foo.default)) %in% c("X", "y", "...")]
  user_specified <- .cl[names(.cl) %in% names(default_args)]
  full_call_args <- c(as.list(.cl), default_args[!names(default_args) %in% names(user_specified)])
  
  # 添加标准...标记,修正调用名称
  full_call_args$... <- quote(...)
  full_call_args[[1]] <- as.name("foo")
  
  # 替换结果的call属性
  attr(res, "call") <- as.call(full_call_args)
  res
}

foo.default <- function(X, y, bar=FALSE, method='1A', beta=2, ...) {
  .cl <- match.call()
  
  # 合并用户参数与未指定的默认参数
  default_args <- formals(sys.function())[!names(formals(sys.function())) %in% c("X", "y", "...")]
  user_specified <- .cl[names(.cl) %in% names(default_args)]
  full_call_args <- c(as.list(.cl), default_args[!names(default_args) %in% names(user_specified)])
  
  # 清理无效内容,修正调用名称
  full_call_args$... <- quote(...)
  full_call_args[".cl"] <- NULL
  full_call_args[[1]] <- as.name("foo")
  
  `attr<-`(lm.fit(X, y)$coefficients, 'call', as.call(full_call_args))
}

测试验证

运行测试代码后,输出完全符合期望:

foo(X1, y1)
# (Intercept)          hp 
# 30.09886054 -0.06822828 
# attr("call")
# foo(X = X1, y = y1, bar = FALSE, method = "1A", beta = 2, ...)

foo(mpg ~ hp, mtcars)
# (Intercept)          hp 
# 30.09886054 -0.06822828 
# attr("call")
# foo(fo = mpg ~ hp, data = mtcars, bar = FALSE, method = "1A", beta = 2, ...)

关键改动说明

  • 公式方法独立处理调用:不再传递.cl参数,而是在公式方法中生成包含原始fo、data的调用信息,直接替换结果的属性
  • 默认参数合并逻辑:通过formals()提取默认参数,与用户已指定的参数合并,确保未显式设置的默认值都能出现在调用信息中
  • 无效内容清理:移除多余的.cl参数,用quote(...)添加标准的...标记,避免出现无效的... = , NULL表达式

内容的提问来源于stack exchange,提问作者jay.sf

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 18:35:36