如何查看函数的源代码?

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

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

395246 次浏览

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()很有用,因为您不必知道函数来自哪个包。

> 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的函数是在编译后的代码中调用入口点,因此如果您想完全理解这个函数,就必须查看编译后的代码的源代码。.Call5 GitHub镜像的R源代码是一个不错的开始。函数pryr::show_c_source可以是一个有用的工具,因为它会直接带你到一个GitHub页面的.Internal.Primitive调用。包可以使用.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-Forge,或RForge.net),您可能可以在线浏览源代码。

基本包中的编译代码

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

编译代码内置于R解释器

如果你想查看R解释器内置的代码,你需要下载/解包R源代码;或者您可以通过r# EYZ0或温斯顿·张的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()非常适合浏览数据(多列),但对于代码(除了玩具长度之外的任何东西)通常很糟糕。
  • 所以当只想要视图代码时,edit()在我看来实际上比View()要好得多,因为使用edit(),你可以折叠/隐藏/屏蔽所有参数解析/检查/默认/错误消息逻辑,这些逻辑可以占用R函数的70%,只需要了解函数实际操作的部分(!),它的返回类型是什么类型的对象,它是否以及如何递归等等。

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

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

当你使用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()

即可实现相同的功能

在r# EYZ0中有一个非常方便的函数

new_optim <- edit(optim)

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

如果您只是想查看源代码,而不希望烦人的长源代码打印在控制台上,您可以使用它

invisible(edit(optim))

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

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

我不知道这是如何融入主要答案的,但它难住了我一段时间,所以我在这里添加了它:

中缀操作符

要查看一些基本中缀操作符的源代码(例如,%%%*%%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("%%")

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

对于非基元函数,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))

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

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

> functionBody(functionName)

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

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

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

将打开一个带有源代码的新窗格。如果你到达。primitive或。c,你需要另一个方法,抱歉。

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

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


print(f)


function(x){
x * 2
}

首先,尝试运行没有()的函数

示例:让我们获取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!
PyCharm中R-plugin的快速解决方案(对于RStudio,请参阅@Arthur Yip的回答)。 如果需要,键入并在编辑器或R控制台中选择函数名。然后“去申报”;或使用快捷键CTRL-B或Command-B。 注意:这对于.Primitive(即内部实现的)函数是没有用的