我想看看一个函数的源代码,看看它是如何工作的。我知道我可以通过在提示符处输入它的名称来打印一个函数:

> t
function (x) 
UseMethod("t")
<bytecode: 0x2332948>
<environment: namespace:base>

在这种情况下,UseMethod(“t”)是什么意思?我如何找到实际正在使用的源代码,例如:t(1:10)?

当我看到UseMethod和当我看到standardGeneric和showMethods之间有区别吗?

> with
standardGeneric for "with" defined from package "base"

function (data, expr, ...) 
standardGeneric("with")
<bytecode: 0x102fb3fc0>
<environment: 0x102fab988>
Methods may be defined for arguments: data
Use  showMethods("with")  for currently available ones.

在其他情况下,我可以看到R函数正在被调用,但我找不到这些函数的源代码。

> ts.union
function (..., dframe = FALSE) 
.cbind.ts(list(...), .makeNamesTs(...), dframe = dframe, union = TRUE)
<bytecode: 0x36fbf88>
<environment: namespace:stats>
> .cbindts
Error: object '.cbindts' not found
> .makeNamesTs
Error: object '.makeNamesTs' not found

我如何找到像.cbindts和.makeNamesTs这样的函数?

在其他情况下,有一些R代码,但大部分工作似乎是在其他地方完成的。

> matrix
function (data = NA, nrow = 1, ncol = 1, byrow = FALSE, dimnames = NULL) 
{
    if (is.object(data) || !is.atomic(data)) 
        data <- as.vector(data)
    .Internal(matrix(data, nrow, ncol, byrow, dimnames, missing(nrow), 
        missing(ncol)))
}
<bytecode: 0x134bd10>
<environment: namespace:base>
> .Internal
function (call)  .Primitive(".Internal")
> .Primitive
function (name)  .Primitive(".Primitive")

我如何找到。primitive函数做什么?类似地,一些函数调用. c、. call、. fortran、. external或. internal。我怎样才能找到它们的源代码?


当前回答

我不知道这是如何融入主要答案的,但它难住了我一段时间,所以我在这里添加了它:

中缀操作符

要查看一些基本中缀操作符(例如,%%,%*%,%in%)的源代码,请使用getAnywhere,例如:

getAnywhere("%%")
# A single object matching ‘%%’ was found
# It was found in the following places
#   package:base
#   namespace:base
#  with value
#
# function (e1, e2)  .Primitive("%%")

主要的答案包括如何使用镜子来深入挖掘。

其他回答

在R编辑中有一个非常方便的函数

new_optim <- edit(optim)

它将使用R选项中指定的编辑器打开optim的源代码,然后您可以编辑它并将修改后的函数分配给new_optim。我非常喜欢这个函数来查看代码或调试代码,例如,打印一些消息或变量,甚至将它们分配给全局变量以进行进一步研究(当然你可以使用debug)。

如果您只是想查看源代码,而不希望烦人的长源代码打印在控制台上,您可以使用它

invisible(edit(optim))

显然,这不能用于查看C/ c++或Fortran源代码。

顺便说一句,edit可以打开其他对象,如列表、矩阵等,然后显示具有属性的数据结构。函数de可以用来打开一个类似excel的编辑器(如果GUI支持的话)来修改矩阵或数据帧并返回新的。这有时很方便,但在通常情况下应该避免,特别是当你的矩阵很大的时候。

首先,尝试在没有()的情况下运行函数

示例:让我们获取cat()函数的源代码:

cat
    if (is.character(file)) 
        if (file == "") 
            file <- stdout()
        else if (startsWith(file, "|")) {
            file <- pipe(substring(file, 2L), "w")
            on.exit(close(file))
        }
        else {
            file <- file(file, ifelse(append, "a", "w"))
            on.exit(close(file))
        }
    .Internal(cat(list(...), file, sep, fill, labels, append))

但有时它会返回“UseMethod”而不是源代码

如果我们试图获取read_xml()的源代码:

library(xml2)
read_xml 
# UseMethod("read_xml")

那对我们没什么用处!在这种情况下,看看这些方法:

methods("read_xml")
# read_xml.character* read_xml.connection* read_xml.raw* read_xml.response* 

并使用getAnywhere对上面的值查看源代码:

getAnywhere("read_xml.character")

另一个例子

让我们试着看看qqnorm()的源代码:

qqnorm
# UseMethod("qqnorm") # Not very useful

methods("qqnorm")
# [1] qqnorm.default* # Making progress...

getAnywhere("qqnorm.default") # Shows source code!

对于非原语函数,R基包含一个名为body()的函数,该函数返回函数体。例如,print.Date()函数的源代码可以查看:

body(print.Date)

会产生这样的结果:

{
    if (is.null(max)) 
        max <- getOption("max.print", 9999L)
    if (max < length(x)) {
        print(format(x[seq_len(max)]), max = max, ...)
        cat(" [ reached getOption(\"max.print\") -- omitted", 
            length(x) - max, "entries ]\n")
    }
    else print(format(x), max = max, ...)
    invisible(x)
}

如果您正在编写脚本,并希望函数代码作为字符向量,则可以获得它。

capture.output(print(body(print.Date)))

会让你:

[1] "{"                                                                   
[2] "    if (is.null(max)) "                                              
[3] "        max <- getOption(\"max.print\", 9999L)"                      
[4] "    if (max < length(x)) {"                                          
[5] "        print(format(x[seq_len(max)]), max = max, ...)"              
[6] "        cat(\" [ reached getOption(\\\"max.print\\\") -- omitted\", "
[7] "            length(x) - max, \"entries ]\\n\")"                      
[8] "    }"                                                               
[9] "    else print(format(x), max = max, ...)"                           
[10] "    invisible(x)"                                                    
[11] "}"     

我为什么要这么做?我正在创建一个自定义S3对象(x,其中类(x) = "foo")基于一个列表。列表成员之一(名为“fun”)是一个函数,我想打印.foo()显示缩进的函数源代码。所以我最终在print.foo()中得到了以下片段:

sourceVector = capture.output(print(body(x[["fun"]])))
cat(paste0("      ", sourceVector, "\n"))

它缩进并显示与x[["fun"]]相关的代码。

编辑2020-12-31

获得源代码相同字符向量的一种不那么迂回的方法是:

sourceVector = deparse(body(x$fun))

当您使用debug()函数进行调试时,它会显示出来。 假设您希望看到t()转置函数中的底层代码。仅仅输入“t”,并不能透露太多信息。

>t 
function (x) 
UseMethod("t")
<bytecode: 0x000000003085c010>
<environment: namespace:base>

但是,使用'debug(functionName)',它会显示底层代码,而不是内部代码。

> debug(t)
> t(co2)
debugging in: t(co2)
debug: UseMethod("t")
Browse[2]> 
debugging in: t.ts(co2)
debug: {
    cl <- oldClass(x)
    other <- !(cl %in% c("ts", "mts"))
    class(x) <- if (any(other)) 
        cl[other]
    attr(x, "tsp") <- NULL
    t(x)
}
Browse[3]> 
debug: cl <- oldClass(x)
Browse[3]> 
debug: other <- !(cl %in% c("ts", "mts"))
Browse[3]> 
debug: class(x) <- if (any(other)) cl[other]
Browse[3]>  
debug: attr(x, "tsp") <- NULL
Browse[3]> 
debug: t(x)

编辑: Debugonce()无需使用undebug()即可实现相同的功能。

您还可以尝试使用print.function(),它是S3通用的,以便在控制台中写入该函数。