首页 文章

如何查看函数的源代码?

提问于
浏览
440

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

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

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

当我看到 UseMethod 和我看到 standardGenericshowMethods 时,和 with 之间有区别吗?

> 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 . 我如何找到这些的源代码?

9 回答

  • 4

    除了这个问题及其重复的其他答案之外,这里有's a good way to get source code for a package function without needing to know which package it' s . 如果我们想要 randomForest::rfcv() 的来源:

    view/edit 它在弹出窗口中:

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

    redirect to a separate file

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

    对于非原始函数,R base包含一个函数 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 ,其中 class(x) = "foo" ) . 其中一个列表成员(名为"fun")是一个函数,我希望 print.foo() 显示函数源代码,缩进 . 所以我在 print.foo() 中得到了以下代码:

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

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

  • 81

    您也可以尝试使用 print.function() (S3泛型)来在控制台中编写函数 .

  • 24

    R edit 有一个非常方便的功能

    new_optim <- edit(optim)
    

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

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

    invisible(edit(optim))
    

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

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

  • 16

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

  • 14

    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
    

    "Non-visible functions are asterisked"表示该函数未从其包的名称空间中导出 . 您仍然可以通过 ::: 函数(即 stats:::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.makeNamesTsstats 命名空间中的未导出函数 . 您可以使用 ::: 运算符或 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"不引用由 compiler 包创建的字节编译的R代码 . 上面输出中的 <bytecode: 0x294e410> 行表示该函数是字节编译的,您仍然可以从R命令行查看源 .

    调用 .C.Call.Fortran 的函数, .External.Internal.Primitive 正在编译代码中调用入口点,因此如果要完全理解该函数,则必须查看已编译代码的源代码 . This 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() 函数可以为您获取包源 .

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

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

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

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

    基础包中的编译代码

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

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

    如果要查看R解释器内置的代码,则需要下载/解压缩R源代码;或者您可以通过R Subversion repositoryWinston Chang's github mirror在线查看来源 .

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

  • 8

    只要该函数是用纯R而不是C / C / Fortran编写的,就可以使用以下内容 . 否则最好的方法是 debugging 并使用“ jump into ”:

    > functionBody(functionName)
    
  • 3

    使用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)
    

    EDIT: debugonce()完成相同操作而不必使用undebug()

  • 3

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

    中缀运营商

    要查看某些基础中缀运算符的源代码(例如, %%%*%%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("%%")
    

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

相关问题