我如何查看函数的源代码?
我想看一个函数的源代码,看看它是如何工作的。 我知道我可以通过在提示符下键入它的名字来打印一个函数:
> 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的版本。示例包括base
, compiler
, stats
和utils
。 因此,如上所述,它们不可在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]。 只读代码将在编辑器中打开。