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

> 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。我怎样才能找到它们的源代码?


当前回答

UseMethod("t")告诉您t()是一个(S3)泛型函数,它具有用于不同对象类的方法。

S3方法调度系统

对于S3类,可以使用methods函数列出特定泛型函数或类的方法。

> methods(t)
[1] t.data.frame t.default    t.ts*       

   Non-visible functions are asterisked
> methods(class="ts")
 [1] aggregate.ts     as.data.frame.ts cbind.ts*        cycle.ts*       
 [5] diffinv.ts*      diff.ts          kernapply.ts*    lines.ts        
 [9] monthplot.ts*    na.omit.ts*      Ops.ts*          plot.ts         
[13] print.ts         time.ts*         [<-.ts*          [.ts*           
[17] t.ts*            window<-.ts*     window.ts*      

   Non-visible functions are asterisked

“带星号的非可见函数”表示函数没有从其包的命名空间导出。您仍然可以通过:::函数(即stats:::t.t ts)或使用getAnywhere()查看其源代码。getAnywhere()很有用,因为您不必知道函数来自哪个包。

> getAnywhere(t.ts)
A single object matching ‘t.ts’ was found
It was found in the following places
  registered S3 method for t from namespace stats
  namespace:stats
with value

function (x) 
{
    cl <- oldClass(x)
    other <- !(cl %in% c("ts", "mts"))
    class(x) <- if (any(other)) 
        cl[other]
    attr(x, "tsp") <- NULL
    t(x)
}
<bytecode: 0x294e410>
<environment: namespace:stats>

S4方法调度系统

S4系统是一种较新的方法调度系统,是S3系统的替代方案。下面是一个S4函数的例子:

> library(Matrix)
Loading required package: lattice
> chol2inv
standardGeneric for "chol2inv" defined from package "base"

function (x, ...) 
standardGeneric("chol2inv")
<bytecode: 0x000000000eafd790>
<environment: 0x000000000eb06f10>
Methods may be defined for arguments: x
Use  showMethods("chol2inv")  for currently available ones.

输出已经提供了大量信息。standardGeneric是S4函数的指示器。提供了查看已定义S4方法的有用方法:

> showMethods(chol2inv)
Function: chol2inv (package base)
x="ANY"
x="CHMfactor"
x="denseMatrix"
x="diagonalMatrix"
x="dtrMatrix"
x="sparseMatrix"

getMethod可以用来查看其中一个方法的源代码:

> getMethod("chol2inv", "diagonalMatrix")
Method Definition:

function (x, ...) 
{
    chk.s(...)
    tcrossprod(solve(x))
}
<bytecode: 0x000000000ea2cc70>
<environment: namespace:Matrix>

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

例如,对于每个方法,还有一些方法具有更复杂的签名

require(raster)
showMethods(extract)
Function: extract (package raster)
x="Raster", y="data.frame"
x="Raster", y="Extent"
x="Raster", y="matrix"
x="Raster", y="SpatialLines"
x="Raster", y="SpatialPoints"
x="Raster", y="SpatialPolygons"
x="Raster", y="vector"

要查看其中一个方法的源代码,必须提供完整的签名。

getMethod("extract" , signature = c( x = "Raster" , y = "SpatialPolygons") )

仅提供部分签名是不够的

getMethod("extract",signature="SpatialPolygons")
#Error in getMethod("extract", signature = "SpatialPolygons") : 
#  No method found for function "extract" and signature SpatialPolygons

调用未导出函数的函数

在ts.union的情况下,.cbindts和. makenamests是stats名称空间中未导出的函数。您可以使用:::operator或getAnywhere查看未导出函数的源代码。

> stats:::.makeNamesTs
function (...) 
{
    l <- as.list(substitute(list(...)))[-1L]
    nm <- names(l)
    fixup <- if (is.null(nm)) 
        seq_along(l)
    else nm == ""
    dep <- sapply(l[fixup], function(x) deparse(x)[1L])
    if (is.null(nm)) 
        return(dep)
    if (any(fixup)) 
        nm[fixup] <- dep
    nm
}
<bytecode: 0x38140d0>
<environment: namespace:stats>

调用已编译代码的函数

注意,“compiled”并不是指编译器包创建的字节编译的R代码。上面输出中的<bytecode: 0x294e410>行表示该函数是字节编译的,并且您仍然可以从R命令行查看源代码。

Functions that call .C, .Call, .Fortran, .External, .Internal, or .Primitive are calling entry points in compiled code, so you will have to look at sources of the compiled code if you want to fully understand the function. This GitHub mirror of the R source code is a decent place to start. The function pryr::show_c_source can be a useful tool as it will take you directly to a GitHub page for .Internal and .Primitive calls. Packages may use .C, .Call, .Fortran, and .External; but not .Internal or .Primitive, because these are used to call functions built into the R interpreter.

Calls to some of the above functions may use an object instead of a character string to reference the compiled function. In those cases, the object is of class "NativeSymbolInfo", "RegisteredNativeSymbol", or "NativeSymbol"; and printing the object yields useful information. For example, optim calls .External2(C_optimhess, res$par, fn1, gr1, con) (note that's C_optimhess, not "C_optimhess"). optim is in the stats package, so you can type stats:::C_optimhess to see information about the compiled function being called.

包中编译的代码

如果您想查看包中已编译的代码,则需要下载/解包包源代码。安装的二进制文件不够用。包的源代码可以从最初安装包的同一个CRAN(或CRAN兼容)存储库中获得。package()函数可以为您获取包的源代码。

download.packages(pkgs = "Matrix", 
                  destdir = ".",
                  type = "source")

这将下载Matrix包的源版本,并将相应的.tar.gz文件保存在当前目录中。编译函数的源代码可以在未压缩和未tared文件的src目录中找到。解压缩和解压缩步骤可以在R外部完成,也可以在R内部使用untar()函数完成。可以将下载和扩展步骤合并到一个调用中(注意,以这种方式一次只能下载和解包一个包):

untar(download.packages(pkgs = "Matrix",
                        destdir = ".",
                        type = "source")[,2])

或者,如果包开发是公开托管的(例如通过GitHub, R-Forge或RForge.net),你可能可以在线浏览源代码。

基本包中的编译代码

某些包被认为是“基本”包。这些包随R一起发布,它们的版本被锁定在R的版本上。示例包括base、compiler、stats和utils。因此,它们不能像上面描述的那样在CRAN上作为单独的可下载包提供。相反,它们是R源代码树的一部分,位于/src/library/下的各个包目录中。如何访问R源代码将在下一节中描述。

编译代码内置于R解释器

如果你想查看R解释器内置的代码,你需要下载/解包R源代码;或者你可以通过R Subversion存储库或Winston Chang的github镜像在线查看源代码。

Uwe Ligges的R新闻文章(PDF)(第43页)是关于如何查看. internal和. primitive函数源代码的良好参考。基本步骤是首先在src/main/names.c中查找函数名,然后在src/main/*文件中搜索“C-entry”名称。

其他回答

除了关于这个问题的其他答案及其副本之外,还有一种获得包函数源代码的好方法,而不需要知道它在哪个包中。 例如,如果我们想要randomForest::rfcv()的源代码:

在弹出窗口中查看/编辑:

edit(getAnywhere('rfcv'), file='source_rfcv.r')

View(getAnywhere('rfcv'), file='source_rfcv.r')

注意,edit()打开一个文本编辑器(由用户选择),而 View()调用电子表格样式的数据查看器。

View() is great for browsing (multi-columnar) data, but usually terrible for code of anything other than toy length. so when only want to view code, edit() is IMO actually far better than View(), since with edit() you can collapse/hide/dummy out all the arg-parsing/checking/default/error-message logic which can take up to 70% of an R function, and just get to the part where the function actually operationally does something(!), what type(s) of objects its return type is, whether and how it recurses, etc.

重定向到一个单独的文件(这样你就可以在你最喜欢的IDE/编辑器/处理它的grep等):

capture.output(getAnywhere('rfcv'), file='source_rfcv.r')

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

对于非原语函数,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))

只要函数是用纯R而不是C/ c++ /Fortran编写的,就可以使用以下方法。否则,最好的方法是调试和使用“jump into”:

> functionBody(functionName)

PyCharm中R-plugin的快速解决方案(对于RStudio,请参阅@Arthur Yip的回答)。 如果需要,键入并在编辑器或R控制台中选择函数名。然后“转到声明”或使用快捷键CTRL-B或Command-B。 注意:这对于. primitive(即内部实现的)函数是没有用的。