如何查看函数的源代码?

How can I view the source code for a function?

提问人:Joshua Ulrich 提问时间:10/7/2013 最后编辑:WaelJoshua Ulrich 更新时间:2/15/2023 访问量:427252

问:

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

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

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

当我看到和当我看到和 时之间有区别吗?UseMethodstandardGenericshowMethodswith

> 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

函数 调试 R-FAQ

评论

2赞 Richie Cotton 10/7/2013
另请参阅 stackoverflow.com/q/1439348/134830
1赞 Ben Bolker 10/8/2013
另请参阅 stackoverflow.com/questions/14035506/...
0赞 Mark Miller 7/8/2015
另请参阅 stackoverflow.com/questions/9385411/...

答:

639赞 19 revs, 8 users 54%Joshua Ulrich #1

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

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.tsgetAnywhere()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.

输出已经提供了很多信息。 是 S4 函数的指示器。提供了查看定义的 S4 方法的方法很有帮助:standardGeneric

> 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>

调用已编译代码的函数

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

在编译代码中调用 、 、 、 或正在调用入口点的函数,因此,如果要完全理解该函数,则必须查看已编译代码的源代码。R 源代码的 GitHub 镜像是一个不错的起点。该函数可能是一个有用的工具,因为它会将您直接带到 GitHub 页面进行调用。软件包可以使用 、 、 和 ;但不是 或 ,因为这些函数用于调用 R 解释器中内置的函数。.C.Call.Fortran.External.Internal.Primitivepryr::show_c_source.Internal.Primitive.C.Call.Fortran.External.Internal.Primitive

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

包中的编译代码

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

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

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

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

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

基础包中的编译代码

某些包被视为“基本”包。这些包随 R 一起提供,并且其版本锁定到 R 版本。示例包括 、 、 和 。因此,如上所述,它们不能在 CRAN 上作为单独的可下载包提供。相反,它们是 R 源代码树的一部分,位于 下的各个包目录中。下一节将介绍如何访问 R 源。basecompilerstatsutils/src/library/

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

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

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

评论

94赞 Ari B. Friedman 10/27/2013
如果您使用 ,它将尝试拉取您的文本光标所在的函数的源,如果您按下该键。RStudioF2
1赞 Sunny 2/24/2014
@Ari B. Friedman:对不起,这个问题来晚了。RStudio 是否还会为函数拉取 C 源代码,还是只为用 R 编写的函数拉取 C 源代码?谢谢
4赞 Ari B. Friedman 2/25/2014
@Samir我相信这只是 R 源。
2赞 JimLohse 3/27/2018
模仿是最真诚的奉承形式,我认为这个答案/维基是第一位的:)在此之前 rfaqs.com/source-code-of-r-method
2赞 Eli Holmes 8/27/2021
唉,已弃用,不再可用。替换它的帮助文件未显示如何获取 S4 方法的源代码。getMethod()findMethods()
114赞 smci 5/23/2014 #2

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

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

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

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

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

  • View()非常适合浏览(多列)数据,但对于玩具长度以外的任何代码通常很糟糕。
  • 所以当只想查看代码时,IMO 实际上比 要好得多,因为您可以折叠/隐藏/虚拟所有 arg-parsing/checking/default/error-message 逻辑,这些逻辑最多可以占用 R 函数的 70%,并且只是到达函数实际操作执行某些操作的部分(!),它的返回类型是什么类型的对象, 它是否以及如何递归等。edit()View()edit()

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

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

评论

0赞 smci 8/22/2015
诚然,getAnywhere 是另一个古怪的 R 名称选择,它应该被称为 findOnSearchPath 或类似名称。
1赞 Sigfried 2/28/2019
我会对这个答案投赞成票,因为它让我接近了我想要的。在 RStudio 中,我真正想要的是;其中是来自已加载包的函数。View(foo)foo
1赞 smci 3/4/2019
@Sigfried:edit() 打开一个文本编辑器(用户选择),而 View() 打开一个 Excel 类型的电子表格查看来查看数据,后者适合浏览(多列)数据,但对于玩具长度以外的任何代码来说通常很糟糕。例如,正如我所暗示的,通常在浏览函数时,我想做的第一件事是跳过/折叠/虚拟掉所有参数解析和默认操作逻辑,以查看函数的实际作用
0赞 smci 7/29/2020
@Sigfried:更新以包含所有这些备注/提示。
31赞 Selva 8/12/2014 #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)

编辑:debugonce() 无需使用 undebug() 即可完成相同的操作

评论

1赞 Brian Diggs 8/12/2014
与公认的答案中给出的方法相比,这种方法的缺点是您需要一个工作函数调用(指定所有必要的参数,可接受);而且,除了初始代码块之外,您还可以在运行时获得每个块。这对于调试非常有用,但对于仅获取源代码来说并不是最佳选择。
0赞 Selva 8/14/2014
是的,它不是最佳的。但是,如果您很聪明,您可以快速而肮脏地获得源代码,尤其是内置功能。
5赞 Joshua Ulrich 8/19/2014
在这种情况下,我还建议使用 T代替。debugoncedebug
14赞 Eric 12/5/2014 #4

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

new_optim <- edit(optim)

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

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

invisible(edit(optim))

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

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

评论

3赞 Brian Diggs 12/5/2014
此方法仅显示打印函数给出的相同函数源(即,与问题中的相同)。比这更进一步/更深入是这个问题的意义所在。
2赞 Eric 12/6/2014
@BrianDiggs 是的,你是对的。我并不是要回答这个问题,因为约书亚已经给出了一个相当完整的答案。我只是尝试添加一些与该主题相关的东西,有趣并且可能有用。
0赞 smci 7/29/2020
对不起,我在 7 个月前发布了这个。不过,使用是一个很好的提示,还有“不适用于 C/C++ 或 Fortran”的评论。invisible(edit(...))
22赞 MichaelChirico 12/2/2015 #5

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

中缀运算符

要查看一些基本中缀运算符(例如,,),请使用 ,例如:%%%*%%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("%%")

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

评论

8赞 Joshua Ulrich 12/2/2015
SMCI的回答推荐。或者,如果您已经知道运算符的名称,则可以只使用反引号:.getAnywhere`%in%`
3赞 MichaelChirico 12/2/2015
@JoshuaUlrich不知道你可以使用反引号!谢谢。 在你的回答中也提到了,但我认为对中缀的特定引用对于将来参考这个答案很有用——我已经读过很多次这个页面,但仍然有点困惑地试图找到这些函数的代码一段时间——我认为它不适合其他任何一个答案的流程(它们都用于另一个目的)。getAnywheregetAnywhere
34赞 Geoffrey Poole 12/31/2016 #6

对于非基元函数,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 对象 (, where )。其中一个列表成员(名为“fun”)是一个函数,我想显示缩进的函数源代码。所以我最终得到了以下片段:xclass(x) = "foo"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))
6赞 Koo 1/11/2017 #7

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

评论

0赞 Bryan Hanson 11/5/2021
?视图需要一个像对象这样的数据框,它不接受函数(在 base R 中)。您描述的是 RStudio 修改。
9赞 MCH 1/25/2017 #8

只要函数是用纯 C/C++/Fortran 编写的,就可以使用以下函数。否则,最好的方法是调试并使用“跳入”:

> functionBody(functionName)

评论

2赞 Joshua Ulrich 1/25/2017
这与 相同。 是。bodyidentical(functionBody, body)TRUE
1赞 moodymudskipper 11/11/2017
base::body而且,尽管他们不太可能被淘汰。 也可以被覆盖:rdocumentation.org/search?q=bodymethods::functionBodybody
5赞 strboul 12/26/2017 #9

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

评论

5赞 Joshua Ulrich 12/28/2017
print.function()是 S3 方法。泛型是 。直接调用方法通常不是一个好主意。这违背了泛型函数和方法调度的全部目的。print()
15赞 Arthur Yip 2/22/2019 #10

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

  1. 当光标位于任何功能上时,按 F2 键。
  2. 按住时单击函数名称 Ctrl 或 Command
  3. View(function_name)(如上所述)

将打开一个新窗格,其中包含源代码。如果您达到 .Primitive 或 .C 对不起,你需要另一种方法。

2赞 hibou 8/10/2021 #11

要查看函数的源代码,请使用 print()

f <- function(x){
     x * 2
 }

print(f)

function(x){
    x * 2
}
0赞 stevec 5/20/2022 #12

首先,尝试在不()

示例:让我们获取函数的源代码: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!

评论

0赞 Joseph Wood 5/22/2022
你读过@JoshuaUlrich的答案吗?
0赞 stevec 5/22/2022
@JosephWood我做到了,这是我几年前获得这些技术的地方。最近有人问我,为什么当有人运行这个问题/答案时,源代码没有出现,但无法快速弄清楚,所以决定留下我自己的答案,给出一小部分 MRE(尽管有限,没有演示更复杂的情况,例如调用 c/c++/fortran 代码等)。如果我遗漏了什么,或者我错了,请指出,我会修改。qqnorm()qqnorm
0赞 Joseph Wood 5/22/2022
根据我的经验,发布不添加任何新信息的答案通常不会受到很好的欢迎。特别要注意的是,没有免责声明,这本质上是公认的规范答案的浓缩版本。
1赞 stevec 5/22/2022
@JosephWood 你的观点是公平的。我认为简单的答案有时很好(甚至更不错)。我的新手阅读了 30 秒,顶部答案要彻底得多,因此需要更多时间,对于在快速、实用的答案之后的人来说,这可能会令人生畏。我的并不全面,也没有试图解释为什么函数需要不同的方法来发现它们的源代码,相反,它提供了如何获取函数源代码的示例,而不需要对 R 的调度系统了解太多,所以它是简化主义的和更简单的,尽管远没有那么彻底。
0赞 schrödingcöder 7/25/2022 #13

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