Làm thế nào tôi có thể xem mã nguồn cho một chức năng?


551

Tôi muốn xem mã nguồn cho một hàm để xem nó hoạt động như thế nào. Tôi biết tôi có thể in một chức năng bằng cách gõ tên của nó tại dấu nhắc:

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

Trong trường hợp này, UseMethod("t")có nghĩa là gì? Làm cách nào để tìm mã nguồn thực sự đang được sử dụng, ví dụ : t(1:10)?

Có sự khác biệt giữa khi tôi nhìn UseMethodvà khi tôi thấy standardGenericshowMethods, như với 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.

Trong các trường hợp khác, tôi có thể thấy các hàm R đang được gọi, nhưng tôi không thể tìm thấy mã nguồn cho các hàm đó.

> 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

Làm thế nào để tôi tìm thấy các chức năng như .cbindts.makeNamesTs?

Trong các trường hợp khác, vẫn có một chút mã R, nhưng hầu hết các công việc dường như được thực hiện ở một nơi khác.

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

Làm thế nào để tôi tìm ra những .Primitivechức năng làm gì? Tương tự, một số chức năng gọi .C, .Call, .Fortran, .External, hoặc .Internal. Làm thế nào tôi có thể tìm mã nguồn cho những người đó?




Câu trả lời:


518

UseMethod("t")đang nói với bạn rằng đó t()là một hàm chung ( S3 ) có các phương thức cho các lớp đối tượng khác nhau.

Hệ thống điều phối phương thức S3

Đối với các lớp S3, bạn có thể sử dụng methodshàm để liệt kê các phương thức cho một hàm hoặc lớp chung cụ thể.

> 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

"Các chức năng không nhìn thấy được dấu hoa thị" có nghĩa là chức năng này không được xuất từ ​​không gian tên của gói. Bạn vẫn có thể xem mã nguồn của nó thông qua :::chức năng (tức là stats:::t.ts) hoặc bằng cách sử dụng getAnywhere(). getAnywhere()là hữu ích vì bạn không cần phải biết gói chức năng này đến từ đâu.

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

Hệ thống điều phối phương thức S4

Hệ thống S4 là một hệ thống điều phối phương thức mới hơn và là một hệ thống thay thế cho hệ thống S3. Dưới đây là một ví dụ về chức năng 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.

Đầu ra đã cung cấp rất nhiều thông tin. standardGenericlà một chỉ báo của chức năng S4. Phương pháp để xem các phương thức S4 được xác định được cung cấp một cách hữu ích:

> showMethods(chol2inv)
Function: chol2inv (package base)
x="ANY"
x="CHMfactor"
x="denseMatrix"
x="diagonalMatrix"
x="dtrMatrix"
x="sparseMatrix"

getMethod có thể được sử dụng để xem mã nguồn của một trong các phương thức:

> getMethod("chol2inv", "diagonalMatrix")
Method Definition:

function (x, ...) 
{
    chk.s(...)
    tcrossprod(solve(x))
}
<bytecode: 0x000000000ea2cc70>
<environment: namespace:Matrix>

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

Ngoài ra còn có các phương thức có chữ ký phức tạp hơn cho mỗi phương thức, ví dụ

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"

Để xem mã nguồn của một trong các phương thức này, toàn bộ chữ ký phải được cung cấp, vd

getMethod("extract" , signature = c( x = "Raster" , y = "SpatialPolygons") )

Nó sẽ không đủ để cung cấp chữ ký một phần

getMethod("extract",signature="SpatialPolygons")
#Error in getMethod("extract", signature = "SpatialPolygons") : 
#  No method found for function "extract" and signature SpatialPolygons

Các hàm gọi các hàm không được báo cáo

Trong trường hợp ts.union, .cbindts.makeNamesTslà chức năng unexported từ statsnamespace. Bạn có thể xem mã nguồn của các hàm không được báo cáo bằng cách sử dụng :::toán tử hoặc 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>

Các hàm gọi mã được biên dịch

Lưu ý rằng "đã biên dịch" không đề cập đến mã R được biên dịch byte như được tạo bởi gói trình biên dịch . Các <bytecode: 0x294e410>dòng trong kết quả trên chỉ ra rằng chức năng là byte-biên soạn, và bạn vẫn có thể xem mã nguồn từ dòng lệnh R.

Chức năng gọi .C, .Call, .Fortran, .External, .Internal, hoặc .Primitiveđang kêu gọi điểm vào trong mã biên dịch, vì vậy bạn sẽ phải nhìn vào nguồn mã biên dịch nếu bạn muốn hiểu đầy đủ các chức năng. Gương GitHub này của mã nguồn R là một nơi tốt để bắt đầu. Chức năng này pryr::show_c_sourcecó thể là một công cụ hữu ích vì nó sẽ đưa bạn trực tiếp đến trang GitHub để thực hiện .Internal.Primitivegọi. Gói có thể sử dụng .C, .Call, .Fortran, và .External; nhưng không .Internalhoặc .Primitive, bởi vì chúng được sử dụng để gọi các hàm được tích hợp trong trình thông dịch R.

Các lệnh gọi đến một số hàm trên có thể sử dụng một đối tượng thay vì chuỗi ký tự để tham chiếu hàm đã biên dịch. Trong những trường hợp, đối tượng là các lớp "NativeSymbolInfo", "RegisteredNativeSymbol"hoặc "NativeSymbol"; và in đối tượng mang lại thông tin hữu ích. Ví dụ: optimcác cuộc gọi .External2(C_optimhess, res$par, fn1, gr1, con)(lưu ý đó C_optimhess, không phải "C_optimhess"). optimnằm trong gói thống kê, vì vậy bạn có thể nhập stats:::C_optimhessđể xem thông tin về hàm được biên dịch.

Mã tổng hợp trong một gói

Nếu bạn muốn xem mã được biên dịch trong một gói, bạn sẽ cần tải xuống / giải nén nguồn gói. Các tệp nhị phân được cài đặt là không đủ. Mã nguồn của gói có sẵn từ cùng kho lưu trữ CRAN (hoặc tương thích CRAN) mà gói ban đầu được cài đặt từ đó. Các download.packages()chức năng có thể có được nguồn gói cho bạn.

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

Điều này sẽ tải xuống phiên bản nguồn của gói Matrix và lưu .tar.gztệp tương ứng trong thư mục hiện tại. Mã nguồn cho các hàm được biên dịch có thể được tìm thấy trong srcthư mục của tệp không nén và chưa được nén. Bước không nén và giải nén có thể được thực hiện bên ngoài Rhoặc từ bên trong Rbằng cách sử dụng untar()chức năng. Có thể kết hợp bước tải xuống và mở rộng thành một cuộc gọi (lưu ý rằng chỉ có thể tải xuống một gói tại một thời điểm theo cách này):

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

Ngoài ra, nếu việc phát triển gói được lưu trữ công khai (ví dụ qua GitHub , R-Forge hoặc RForge.net ), bạn có thể có thể duyệt mã nguồn trực tuyến.

Mã được biên dịch trong gói cơ sở

Một số gói được coi là gói "cơ sở". Những gói xuất xưởng với R và phiên bản của họ bị khóa lên phiên bản của R. Ví dụ như base, compiler, stats, và utils. Như vậy, chúng không có sẵn dưới dạng các gói có thể tải xuống riêng biệt trên CRAN như được mô tả ở trên. Thay vào đó, chúng là một phần của cây nguồn R trong các thư mục gói riêng lẻ bên dưới /src/library/. Cách truy cập nguồn R được mô tả trong phần tiếp theo.

Mã biên dịch được tích hợp trong trình thông dịch R

Nếu bạn muốn xem mã được tích hợp sẵn cho trình thông dịch R, bạn sẽ cần tải xuống / giải nén các nguồn R; hoặc bạn có thể xem các nguồn trực tuyến thông qua kho lưu trữ R Subversion hoặc gương github của Winston Chang .

Bài báo tin tức R của Uwe Ligges (PDF) (trang 43) là một tài liệu tham khảo chung tốt về cách xem mã nguồn cho .Internalvà các .Primitivechức năng. Các bước cơ bản trước tiên là tìm tên hàm trong src/main/names.cvà sau đó tìm kiếm tên "C-entry" trong các tệp trong src/main/*.


71
Nếu bạn sử dụng RStudio, nó sẽ cố kéo nguồn cho chức năng con trỏ văn bản của bạn kết thúc nếu bạn nhấn F2phím.
Ari B. Friedman

1
@Ari B. Friedman Xin lỗi vì câu hỏi muộn này. RStudio cũng sẽ kéo mã nguồn C cho chức năng hay chỉ cho các chức năng được viết bằng R? Cảm ơn
Nắng

3
@Samir Tôi tin rằng đó chỉ là nguồn R.
Ari B. Friedman

@ AriB.Friedman - cảm ơn Ari, cái này rất tiện dụng. Trong trường hợp của tôi, tôi vẫn cần kiến ​​thức thể hiện trong câu trả lời ( scale, đó là S3 - tôi đã nhận UseMethod("scale")và sau đó sử dụng getAnywhere(scale.default)). Nhưng các chức năng đơn giản chỉ hoạt động tốt.
Tomasz Gandor

2
Bắt chước là hình thức nịnh hót chân thành nhất Tôi giả sử câu trả lời này / wiki xuất hiện đầu tiên :) Trước đó rfaqs.com/source-code-of-r-method
JimLohse

94

Ngoài các câu trả lời khác cho câu hỏi này và các câu hỏi trùng lặp của nó, đây là một cách hay để lấy mã nguồn cho hàm gói mà không cần biết nó thuộc gói nào. Ví dụ: nếu chúng tôi muốn nguồn cho randomForest::rfcv():

Để xem / chỉnh sửa nó trong cửa sổ bật lên:

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

Để chuyển hướng đến một tệp riêng biệt :

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

Phải thừa nhận rằng, getAnywhere là một lựa chọn tên R kỳ quặc khác cho một cái gì đó nên được gọi là findOnSearchPath hoặc tương tự.
smci

1
Tôi sẽ đưa ra câu trả lời này vì nó giúp tôi gần với những gì tôi muốn. Những gì tôi thực sự muốn, trong RStudio, là View(foo); nơi foolà một chức năng từ một gói đã được tải.
Sigfried

1
@Sigfried: edit()mở trình soạn thảo văn bản (theo lựa chọn của người dùng) , trong khi View()mở trình xem bảng tính loại Excel để lấy dữ liệu , trình duyệt này tốt cho việc duyệt dữ liệu (nhiều cột), nhưng thường khủng khiếp đối với mã của bất kỳ thứ gì ngoài chiều dài đồ chơi. Ví dụ như tôi gợi ý, nói chung, điều đầu tiên tôi muốn làm khi duyệt một hàm là bỏ qua / thu gọn / giả tất cả logic phân tích cú pháp và logic hành động mặc định, để xem hàm này thực sự làm gì .
smci

25

Nó được tiết lộ khi bạn gỡ lỗi bằng hàm debug (). Giả sử bạn muốn xem mã bên dưới trong hàm chuyển vị t (). Chỉ cần gõ 't', không tiết lộ nhiều.

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

Nhưng, bằng cách sử dụng 'gỡ lỗi (functionName)', nó tiết lộ mã bên dưới, sans bên trong.

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

EDIT: debugonce () hoàn thành tương tự mà không phải sử dụng undebug ()


Nhược điểm của phương pháp này so với các nhược điểm được đưa ra trong câu trả lời được chấp nhận là bạn cần một lệnh gọi hàm làm việc (tất cả các tham số cần thiết được chỉ định, chấp nhận được); và rằng, ngoài khối mã ban đầu, bạn cũng nhận được từng khối tại thời điểm nó được chạy. Này là rất tốt để gỡ lỗi, nhưng không tối ưu cho chỉ nhận được nguồn.
Brian Diggs

Vâng, nó không tối ưu. Nhưng nếu bạn khéo léo, bạn có thể lấy nguồn nhanh và bẩn, đặc biệt cho các chức năng được xây dựng.
Selva

2
Tôi cũng khuyên bạn nên sử dụng debugoncethay vì debugtrong trường hợp này.
Joshua Ulrich

20

Đối với các hàm không nguyên thủy, cơ sở R bao gồm một hàm được gọi là body()trả về phần thân của hàm. Ví dụ, nguồn của print.Date()hàm có thể được xem:

body(print.Date)

sẽ sản xuất cái này:

{
    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)
}

Nếu bạn đang làm việc trong một tập lệnh và muốn mã chức năng như một vectơ ký tự, bạn có thể lấy nó.

capture.output(print(body(print.Date)))

sẽ có em:

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

Tại sao tôi muốn làm một điều như vậy? Tôi đã tạo một đối tượng S3 tùy chỉnh ( x, ở đâu class(x) = "foo") dựa trên danh sách. Một trong những thành viên trong danh sách (được đặt tên là "vui vẻ") là một hàm và tôi muốn print.foo()hiển thị mã nguồn của hàm, được thụt lề. Vì vậy, tôi đã kết thúc với đoạn trích sau trong print.foo():

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

mà thụt lề và hiển thị mã liên quan đến x[["fun"]].


18

Không thấy làm thế nào điều này phù hợp với dòng câu trả lời chính nhưng nó làm tôi bối rối trong một thời gian vì vậy tôi thêm nó vào đây:

Toán tử Infix

Để xem mã nguồn của một số nhà khai thác cơ sở ghi vào (ví dụ %%, %*%, %in%), sử dụng getAnywhere, ví dụ như:

getAnywhere("%%")
# A single object matching ‘%%’ was found
# It was found in the following places
#   package:base
#   namespace:base
#  with value
#
# function (e1, e2)  .Primitive("%%")

Câu trả lời chính bao gồm làm thế nào để sử dụng gương để đào sâu hơn.


6
đề nghị trả lời của smcigetAnywhere . Hoặc bạn chỉ có thể sử dụng backticks nếu bạn đã biết tên của toán tử : `%in%`.
Joshua Ulrich

3
@JoshuaUlrich không biết bạn có thể sử dụng backticks! Cảm ơn. getAnywherecũng được đề cập trong câu trả lời của bạn, nhưng tôi nghĩ rằng một tài liệu tham khảo cụ thể về infix rất hữu ích cho việc tham khảo trong tương lai cho câu trả lời này - Tôi đã đọc trang này nhiều lần và vẫn hơi bối rối khi cố gắng tìm mã cho các hàm như vậy cho trong khi - và tôi không nghĩ nó phù hợp với dòng câu trả lời khác (cả hai đều sử dụng getAnywherecho mục đích khác).
MichaelChirico

10

Có một chức năng rất tiện dụng trong R edit

new_optim <- edit(optim)

Nó sẽ mở mã nguồn optimsử dụng trình soạn thảo được chỉ định trong R options, và sau đó bạn có thể chỉnh sửa nó và gán chức năng đã sửa đổi new_optim. Tôi rất thích chức năng này để xem mã hoặc gỡ lỗi mã, ví dụ: in một số thông báo hoặc biến hoặc thậm chí gán chúng cho một biến toàn cục để điều tra thêm (tất nhiên bạn có thể sử dụng debug).

Nếu bạn chỉ muốn xem mã nguồn và không muốn mã nguồn dài gây phiền nhiễu được in trên bảng điều khiển của mình, bạn có thể sử dụng

invisible(edit(optim))

Rõ ràng, điều này không thể được sử dụng để xem mã nguồn C / C ++ hoặc Fortran.

BTW, editcó thể mở các đối tượng khác như danh sách, ma trận, v.v., sau đó hiển thị cấu trúc dữ liệu với các thuộc tính. Chức năng decó thể được sử dụng để mở excel như trình soạn thảo (nếu GUI hỗ trợ) để sửa đổi ma trận hoặc khung dữ liệu và trả về cái mới. Điều này đôi khi rất tiện lợi, nhưng nên tránh trong trường hợp thông thường, đặc biệt là khi ma trận của bạn lớn.


3
Cách tiếp cận này chỉ đưa ra cùng một nguồn chức năng mà việc in hàm đưa ra (nghĩa là giống như trong câu hỏi). Nhận được nhiều hơn / sâu hơn đó là những gì câu hỏi này là về.
Brian Diggs

2
@BrianDiggs Vâng, bạn nói đúng. Tôi không có ý đưa ra câu trả lời cho câu hỏi, vì Joshua đã đưa ra một câu trả lời khá đầy đủ. Tôi chỉ cố gắng thêm một cái gì đó liên quan đến chủ đề, thú vị và có thể hữu ích để biết về.
Eric

8

Miễn là hàm được viết bằng R thuần túy không phải C / C ++ / Fortran, người ta có thể sử dụng như sau. Mặt khác, cách tốt nhất là gỡ lỗi và sử dụng " nhảy vào ":

> functionBody(functionName)

2
Điều này giống như body. identical(functionBody, body)TRUE.
Joshua Ulrich

1
base::bodymethods::functionBody, mặc dù họ không giống như bị ghê tởm. bodycũng có thể bị ghi đè: rdocumentation.org/search?q=body
Moody_Mudskipper

7

Trong RStudio, có (ít nhất) 3 cách:

  1. Nhấn phím F2 trong khi con trỏ ở trên bất kỳ chức năng nào.
  2. Nhấp vào tên hàm trong khi giữ Ctrl hoặc Command
  3. View(function_name) (như đã nêu ở trên)

Một khung mới sẽ mở với mã nguồn. Nếu bạn đạt .Primitive hoặc .C, bạn sẽ cần một phương pháp khác, xin lỗi.


5

View([function_name])- ví dụ. View(mean)Đảm bảo sử dụng chữ hoa [V]. Mã chỉ đọc sẽ mở trong trình chỉnh sửa.


5

Bạn cũng có thể thử sử dụng print.function()chung chung S3, để có được chức năng ghi trong bảng điều khiển.


3
print.function()là một phương thức S3 . Các chung là print(). Và nói chung không nên gọi phương thức trực tiếp. Điều đó đánh bại toàn bộ mục đích của các hàm chung và phương thức gửi.
Joshua Ulrich
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.