通缉:如果无法找到对象,则为S3通用主体提供优雅的解决方案

时间:2014-09-24 10:55:51

标签: r oop generics methods

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

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

我首先将S3泛型定义如下

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

为了从x data中提取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) } ,就像我在这里一样

newMethod.default

但我不太确定这是普通的还是好的做法,部分原因是it's suggested not to use the second argument of UseMethod。另外,我需要保持{{1}}的第一行不变,否则我会再次收到错误消息。

如果有人能指出我正确的方向,我会非常感激,因为我觉得我并没有在这个方向上走得很好。非常感谢提前!

1 个答案:

答案 0 :(得分:1)

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

查看with()函数和defaut方法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方法用于类的组合甚至是缺少参数。但是只要你留在S3中,除了在泛型函数中使用exists()之外,没有办法捕获不存在的对象,例如:

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

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