我如何查看函数的源代码?

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

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

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

在其他情况下,我可以看到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 

我如何find像.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.Fortran.Internal 。 我怎样才能find这些的源代码?

UseMethod("t")告诉你t()是一个( S3 )generics函数,它具有用于不同对象类的方法。

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系统的替代scheme。 这里是一个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> 

调用编译代码的函数

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

调用.C.Call.Call.Fortran.Internal.Primitive正在调用编译代码中的入口点,因此如果要充分理解函数,则必须查看已编译代码的来源。 R源代码的GitHub镜像是一个体面的开始。 函数pryr::show_c_source可以是一个有用的工具,因为它会直接将您带到.Internal.Primitive调用的GitHub页面。 包可能使用.C.Call.Fortran.Fortran ; 但不是.Internal.Primitive ,因为这些是用来调用内置到R解释器中的函数的。

调用上述某些函数可能会使用一个对象而不是string来引用编译后的函数。 在这些情况下,对象是类"NativeSymbolInfo""RegisteredNativeSymbol""NativeSymbol" ; 并打印对象产生有用的信息。 例如, optim调用.External2(C_optimhess, res$par, fn1, gr1, con) (注意C_optimhess ,而不是"C_optimhess" )。 optim在stats包中,所以你可以inputstats:::C_optimhess来查看被调用的编译函数的信息。

编译包中的代码

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

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

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

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

另外,如果软件包开发是公开的(例如通过GitHub , R-Forge或RForge.net ),你可以在线浏览源代码。

将代码编译在基本包中

某些软件包被视为“基础”软件包。 这些软件包随R一起提供,并且它们的版本被locking到R的版本。示例包括basecompilerstatsutils 。 因此,如上所述,它们不可在CRAN上作为单独的可下载包提供。 相反,它们是/src/library/下独立包目录中R源树的一部分。 下一节将介绍如何访问R源。

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

如果你想查看R解释器的内置代码,你将需要下载/解压R源代码; 也可以通过R Subversion版本库或Winston Chang的github镜像在线查看源代码。

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

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

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

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

redirect到一个单独的文件

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

当你使用debug()函数进行debugging时,它会显示出来。 假设你想看t()转置函数中的底层代码。 只要input“t”,不会显示太多。

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

但是,使用“debugging(函数名)”,它揭示了底层的代码,没有内部。

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

没有看到这是如何融入主stream答案的stream程,但它困难了一段时间,所以我在这里添加它:

中缀操作员

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

如果你在一个脚本中工作,并希望function代码作为一个字符向量,你可以得到它。

 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] "}" 

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

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

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

R edit有一个非常方便的function

 new_optim <- edit(optim) 

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

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

 invisible(edit(optim)) 

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

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

只要函数是用纯R而不是C / C ++ / Fortran编写的,就可以使用下面的代码。 否则最好的方法是debugging和使用“ 跳入 ”:

 > functionBody(functionName) 

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