r语言 - 需要:如果找不到对象,则为 S3 通用体提供优雅的解决方案



这只是我正在编写的更复杂代码的MWE。

假设我想创建一个新方法newMethod,它(除其他外)需要从数据帧data中提取变量x,但也需要从data中提取一些附加属性(例如,它的列名)。

我首先将S3泛型定义如下

newMethod <- function(x, data) UseMethod("newMethod")

为了从data中提取x,我编写了newMethod.default:

newMethod.default <- function(x, data) {
  x <- eval(substitute(x), envir = data)
  return(list(x = x, names = names(data)))
}

根据class(x),我计划进行进一步的调度(可能在返回列表之前,在newMethod.default内部),但这在这里并不重要(我只是在这里提到它,以解释为什么我首先要创建一个泛型函数)。

然而,当我通过运行来测试这个通用

newMethod(mpg, mtcars)

我收到(也许并不奇怪)错误消息:Error in newMethod(mpg, mtcars) : object 'mpg' not found。所以现在我想知道什么是最好的解决办法。我的直觉是将部分代码从newMethod.default移动到newMethod,就像我在这里做一样

newMethod <- function(x, data) {
  x <- eval(substitute(x), envir = data)
  UseMethod("newMethod", x)
}

但我不太确定这是常见的还是好的做法,部分原因是建议不要使用UseMethod的第二个自变量。此外,我需要保持newMethod.default的第一行不变,否则我将再次收到错误消息。

如果有人能为我指明正确的方向,我将不胜感激,因为我觉得我在这件事上不太对劲。非常感谢!

发生这种情况的原因与方法无关,只与R读取代码的方式有关。它查看了第一个参数,并决定在此基础上进行一些调度。但问题是这个对象mpg不存在,所以R没有办法决定应该调用什么方法。R需要能够确定该对象的类。"不存在"不是一个类,因此不可能对调用函数的环境中不存在的对象进行调度。

看看with()函数和缺省方法with.default()。这基本上就是你在这里所做的,但有点笼统。它的默认方法实际上与您的示例几乎完全相同:

> with.default
function (data, expr, ...) 
eval(substitute(expr), data, enclos = parent.frame())
<bytecode: 0x0825d7d8>
<environment: namespace:base>

重要的部分是with()将数据参数作为第一个参数,将表达式(在您的情况下,仅为mpg)作为第二个参数。这样一来,调用帧中不存在mpg就无关紧要了,因为R在尝试调度时只查看第一个参数。

所以这是有效的:

newMethod <- function( data, x) UseMethod("newMethod")
newMethod.default <- function(data, x) {
  x <- eval(substitute(x), envir = data)
  return(list(x = x, names = names(data)))
}

附带说明:再次考虑newMethod.default内部的调度。不确定你到底想实现什么,但我觉得这是你使用NextMethod的目的。但这是一个棘手的问题,在这种情况下,转移到S4可能会方便得多,原因很简单,你可以对类的组合使用S4方法,甚至对缺少的参数使用S4方法。但是,只要您停留在S3中,就没有办法捕获不存在的对象,除了在泛型函数中使用exists()之外,例如:

newMethod <- function( data, x) {
  if(exists(deparse(substitute(x))))
  UseMethod("newMethod")}

但这是我自己不会使用的代码。

相关内容

最新更新