捕获下一个方法的输出

时间:2018-09-28 13:33:31

标签: r methods

我正在为另一个包中定义的泛型创建S3方法。泛型的较早方法会产生一些控制台输出,这些输出不会作为函数返回值的一部分返回,而只会输出到控制台。我想捕获该输出以用于我自己的方法。

我尝试在capture.output()上使用NextMethod(),但这只会导致一个奇怪的错误:

foo <- function(x, ...) UseMethod("foo")
foo.bar <- function(x, ...) cat(x, "\n")
foo.baz <- function(x, ...) capture.output(NextMethod())

foo(structure(1, class = "bar"))
#> 1
foo(structure(1, class = c("baz", "bar")))
#> Error: 'function' is not a function, but of type 8

这是预期的行为,已知的限制还是错误?通过快速搜索,我找不到与该错误匹配的任何内容。 如何在另一个S3方法中捕获下一个S3方法的输出?

2 个答案:

答案 0 :(得分:2)

这是...“预期行为”。我之所以这样说,是因为我认为这在技术上是正确的,但是用户可能无法期望它。如果您不关心为什么会发生这种情况,而只是想看看如何解决它,请跳到标题“修复”,因为以下有关错误的解释有些涉及。

'function' is not a function, but of type 8是什么意思?

type 8表示类型8 SEXP。来自Section one of the R Internals Manual

  

R用户认为是变量或对象的是符号,它们是   绑定到一个值。该值可以视为SEXP(a   指针)或其指向的结构,即SEXPREC ...

     

当前正在使用SEXPTYPEs 0:10和13:25。...

     

没有SEXPTYPE描述
  ...
  3个CLOSXP封盖
  ...
  8个BUILTINSXP内置函数

NextMethod()期望CLOSXP,而不是BUILTINSXP。如果我们查看do_nextmethod()的{​​{3}}(在第717行附近),这是NextMethod()的C函数

SEXP attribute_hidden do_nextmethod(SEXP call, SEXP op, SEXP args, SEXP env)
{
    // Some code omitted
    if (TYPEOF(s) != CLOSXP){ /* R_LookupMethod looked for a function */
        if (s == R_UnboundValue)
            error(_("no calling generic was found: was a method called directly?"));
        else
            errorcall(R_NilValue,
              _("'function' is not a function, but of type %d"),
              TYPEOF(s));
    }

那为什么在这里发生呢?这是棘手的地方。我相信这是因为通过将NextMethod()通过capture.output(),它是使用内置的eval()来调用的(请参见builtins())。

那么我们该如何处理呢?继续阅读...

修复

我们可以巧妙地使用sink()cat()tempfile()模拟捕获输出:

foo.baz <- function(x, ...) {
    # Create a temporary file to store the output
    tmp <- tempfile("tmp.txt")
    # start sink()
    sink(tmp)
    # call NextMethod() just for the purpose of capturing output
    NextMethod()
    # stop sink'ing
    sink()
    # store the output in an R object
    y <- readLines(tmp)
    # here we'll cat() the output to make sure it worked
    cat("The output was:", y, "\n")
    # destroy the temporary file
    unlink(tmp)
    # and call NextMethod for its actual execution
    NextMethod()
}

foo(structure(1, class = c("baz", "bar")))
# 1 

答案 1 :(得分:0)

我不确定您看到的内容是否已记录:文档?NextMethod清楚地表明它不是常规功能,但我没有遵循所有详细信息来查看您的用法是否会被允许。

一种做自己想要的事的方法就是

foo.baz <- function(x, ...) {class(x) <- class(x)[-1]; capture.output(foo(x, ...))}

这假定该方法是从对泛型的调用中直接调用的;如果存在第三级,它将无法正常工作,并且foo.baz本身是由NextMethod()调用的。