关于r:如何查看函数的源代码?

How can I view the source code for a function?

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

1
2
3
4
5
> t
function (x)
UseMethod("t")
<bytecode: 0x2332948>
<environment: namespace:base>

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

当我看到UseMethod和看到standardGenericshowMethods时,与with一样有区别吗?

1
2
3
4
5
6
7
8
9
> 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函数,但我找不到这些函数的源代码。

1
2
3
4
5
6
7
8
9
> 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代码,但大多数工作似乎在其他地方完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> 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函数列出特定泛型函数或类的方法。

1
2
3
4
5
6
7
8
9
10
11
12
> 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.ts)或使用getAnywhere()查看其源代码。 getAnywhere()非常有用,因为您不必知道函数来自哪个包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
> 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功能的示例:

1
2
3
4
5
6
7
8
9
10
11
> 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方法的方法:

1
2
3
4
5
6
7
8
> showMethods(chol2inv)
Function: chol2inv (package base)
x="ANY"
x="CHMfactor"
x="denseMatrix"
x="diagonalMatrix"
x="dtrMatrix"
x="sparseMatrix"

getMethod可用于查看其中一种方法的源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
> getMethod("chol2inv","diagonalMatrix")
Method Definition:

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

Signatures:
        x              
target "diagonalMatrix"
defined"diagonalMatrix"

例如,对于每种方法,还存在具有更复杂签名的方法

1
2
3
4
5
6
7
8
9
10
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"

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

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

提供部分签名是不够的

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

调用未导出函数的函数

ts.union的情况下,.cbindts.makeNamesTsstats命名空间中的未导出函数。您可以使用:::运算符或getAnywhere查看未导出函数的源代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> 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>

调用编译代码的函数

请注意,"编译"不是指由编译器包创建的字节编译的R代码。上面输出中的行表示该函数是字节编译的,您仍然可以从R命令行查看源。

调用.C.Call.Fortran.External.Internal.Primitive的函数在编译代码中调用入口点,因此如果需要,您必须查看已编译代码的源代码要完全理解这个功能。这个R源代码的GitHub镜像是一个不错的起点。函数pryr::show_c_source可以是一个有用的工具,因为它可以直接进入.Internal.Primitive调用的GitHub页面。包可以使用.C.Call.Fortran.External;但不是.Internal.Primitive,因为它们用于调用内置于R解释器中的函数。

调用上述某些函数可能会使用对象而不是字符串来引用已编译的函数。在这些情况下,对象的类是"NativeSymbolInfo""RegisteredNativeSymbol""NativeSymbol";并打印对象产生有用的信息。例如,optim调用.External2(C_optimhess, res$par, fn1, gr1, con)(注意C_optimhess,而不是"C_optimhess")。 optim位于stats包中,因此您可以键入stats:::C_optimhess以查看有关正在调用的已编译函数的信息。

包中的编译代码

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

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

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

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

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

基础包中的编译代码

某些包被视为"基础"包。这些软件包附带R,其版本锁定为R版本。示例包括basecompilerstatsutils。因此,如上所述,它们不能作为CRAN上的单独可下载包提供。相反,它们是/src/library/下各个包目录中R源代码树的一部分。如何访问R源将在下一节中介绍。

内置于R解释器中的编译代码

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

Uwe Ligges的R新闻文章(PDF)(p.43)是如何查看.Internal.Primitive函数的源代码的一个很好的一般参考。基本步骤是首先在src/main/names.c中查找函数名称,然后在src/main/*中的文件中搜索"C-entry"名称。

好。


除了这个问题的其他答案及其重复之外,这里有一个很好的方法来获取包函数的源代码,而无需知道它所在的包。
例如如果我们想要randomForest::rfcv()的来源:

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

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

要重定向到单独的文件:

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


使用debug()函数进行调试时会显示它。
假设您想在t()转置函数中查看底层代码。只需输入't',就不会显示太多。

1
2
3
4
5
>t
function (x)
UseMethod("t")
<bytecode: 0x000000003085c010>
<environment: namespace:base>

但是,使用'debug(functionName)',它揭示了底层代码,没有内部。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
> 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()


对于非原始函数,R base包含一个名为body()的函数,它返回函数体。例如,可以查看print.Date()函数的来源:

1
body(print.Date)

会产生这个:

1
2
3
4
5
6
7
8
9
10
11
12
{
    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 ]
")
    }
    else print(format(x), max = max, ...)
    invisible(x)
}

如果您正在使用脚本并希望将函数代码作为字符向量,则可以获取它。

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

会得到你:

1
2
3
4
5
6
7
8
9
10
11
12
[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 ]\
")"                      
[8]"    }"                                                              
[9]"    else print(format(x), max = max, ...)"                          
[10]"    invisible(x)"                                                    
[11]"}"

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

1
2
3
sourceVector = capture.output(print(body(x[["fun"]])))
cat(paste0("     ", sourceVector,"
"))

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


没有看到这是如何适应主要答案的流程但它让我困扰了一段时间所以我在这里添加它:

中缀运营商

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

1
2
3
4
5
6
7
8
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 edit中有一个非常方便的功能

1
new_optim <- edit(optim)

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

如果您只想查看源代码并且不希望在控制台上打印恼人的长源代码,则可以使用

1
invisible(edit(optim))

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

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


只要该函数是用纯R而不是C / C ++ / Fortran编写的,就可以使用以下内容。否则最好的方法是调试并使用"jump into":

1
> functionBody(functionName)


您还可以尝试使用print.function()(S3通用)来在控制台中编写函数。


View([function_name]) - 例如View(mean)确保使用大写[V]。只读代码将在编辑器中打开。


在RStudio中,有(至少)3种方式:

  • 光标在任何功能上时按F2键。
  • 按住时单击功能名称
    按Ctrl或Command
  • View(function_name)(如上所述)
  • 将打开一个包含源代码的新窗格。如果你达到.Primitive或.C,你需要另一种方法,抱歉。