代码之家  ›  专栏  ›  技术社区  ›  Bogaso

如何查看可能正在调用C代码的函数内部

c r
  •  0
  • Bogaso  · 技术社区  · 3 年前

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

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

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

    我看到的时候有区别吗 UseMethod 当我看到 standardGeneric showMethods ,与一样 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 。我如何找到这些的源代码?

    0 回复  |  直到 2 年前
        1
  •  2
  •   hibou    3 年前

    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.ts ),或使用 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 命名空间。可以使用查看未导出函数的源代码 ::: 操作员或 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命令行查看源代码。

    调用的函数 .C , .Call , .Fortran , .External , .Internal .Primitive 正在调用已编译代码中的入口点,所以如果您想完全理解该函数,就必须查看已编译代码的源代码。 This GitHub镜像R源代码是一个不错的起点。函数 pryr::show_c_source 可以是一个有用的工具,因为它将直接带你进入GitHub页面 内部的 原始的 电话。程序包可能使用 C , 呼叫 , .FFortran 外部的 ; 但不是 内部的 原始的 ,因为这些函数用于调用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])
    

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

    在基本包中编译的代码

    某些软件包被视为“基础”软件包。这些软件包随R一起发货,其版本锁定为R版本。示例包括 base , compiler , 统计数据 utils 因此,如上所述,它们不能作为单独的可下载包在CRAN上提供。相反,它们是下各个包目录中R源代码树的一部分 /src/library/ 。如何访问R源将在下一节中进行描述。

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

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

    Uwe Ligges的 R news article (PDF) (第43页)是如何查看的源代码的一个很好的通用参考 内部的 原始的 功能。基本步骤是首先在中查找函数名称 src/main/names.c 然后在中的文件中搜索“C-entry”名称 src/main/* .

        2
  •  1
  •   stevec Zxeenu    3 年前

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

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

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

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

    • 看法 非常适合浏览(多栏) 数据 ,但通常很糟糕 密码 除了玩具长度以外的任何东西。
    • 所以当你只想 视图代码 , 编辑 IMO实际上比 看法 ,由于 编辑 您可以折叠/隐藏/伪出所有的arg解析/检查/默认/错误消息逻辑,这些逻辑最多可以占用R函数的70%,只需进入函数实际操作的部分(!),它的返回类型是什么类型的对象,它是否以及如何递归,等等。

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

    capture.output(getAnywhere('rfcv'), file='source_rfcv.r')
    
        3
  •  0
  •   schrödingcöder    2 年前

    对于非基函数,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 哪里 class(x) = "foo" )基于列表。其中一个成员(名为“fun”)是一个函数,我想要 print.foo() 以缩进显示函数源代码。所以我在 print.foo() :

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

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

    编辑2020-12-31

    一种不那么迂回的方式 character 源代码的矢量为:

    sourceVector = deparse(body(x$fun))