代码之家  ›  专栏  ›  技术社区  ›  Mikko Marttila

捕获下一个方法的输出

  •  0
  • Mikko Marttila  · 技术社区  · 6 年前

    我正在为另一个包中定义的泛型创建一个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 回复  |  直到 6 年前
        1
  •  3
  •   duckmayr    6 年前

    这是……”我之所以这么说,是因为我相信这在技术上是正确的,但用户可能没有必要期待它。如果您不关心为什么会发生这种情况,只想看看如何解决它,请跳到标题“修复”,因为下面的错误解释有点牵扯其中。

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

    type 8 指类型8 SEXP Section one of the R Internals Manual :

    R用户所认为的变量或对象是 绑定到一个值。这个值可以看作是一个SEXP(a 指针),或者它指向的结构,一个SEXPREC。。。

    无性别类型描述

    3个CLOSXP闭包
    ...

    NextMethod() CLOSXP BUILTINSXP . 如果我们看看 source code do_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));
        }
    

    那为什么会发生在这里呢?这就是它变得棘手的地方。我相信是因为路过 通过 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 
    
        2
  •  1
  •   user2554330    6 年前

    ?NextMethod 说明它不是一个常规函数,但我没有按照所有细节查看是否允许使用。

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

    这假设方法是直接从泛型调用调用的;如果有第三层就不行了,而且 foo.baz 本身被 NextMethod() .