Bagaimana saya bisa melihat kode sumber untuk suatu fungsi?

551

Saya ingin melihat kode sumber untuk suatu fungsi untuk melihat cara kerjanya. Saya tahu saya bisa mencetak fungsi dengan mengetik namanya saat diminta:

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

Dalam hal ini, apa UseMethod("t")artinya? Bagaimana cara menemukan kode sumber yang sebenarnya digunakan oleh, misalnya t(1:10):?

Apakah ada perbedaan antara ketika saya melihat UseMethoddan ketika saya melihat standardGenericdan showMethods, seperti halnya 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.

Dalam kasus lain, saya dapat melihat bahwa fungsi R dipanggil, tetapi saya tidak dapat menemukan kode sumber untuk fungsi tersebut.

> 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

Bagaimana cara menemukan fungsi seperti .cbindtsdan .makeNamesTs?

Masih dalam kasus lain, ada sedikit kode R, tetapi sebagian besar pekerjaan tampaknya dilakukan di tempat lain.

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

Bagaimana saya mencari tahu apa .Primitivefungsinya? Demikian pula, beberapa fungsi panggilan .C, .Call, .Fortran, .External, atau .Internal. Bagaimana saya dapat menemukan kode sumber untuk itu?

Joshua Ulrich
sumber
2
Lihat juga stackoverflow.com/q/1439348/134830
Richie Cotton

Jawaban:

518

UseMethod("t")memberitahu Anda bahwa itu t()adalah fungsi generik ( S3 ) yang memiliki metode untuk kelas objek yang berbeda.

Sistem pengiriman metode S3

Untuk kelas S3, Anda dapat menggunakan methodsfungsi ini untuk membuat daftar metode untuk fungsi atau kelas generik tertentu.

> 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

"Fungsi yang tidak terlihat ditandai bintang" berarti fungsi tersebut tidak diekspor dari namespace paketnya. Anda masih dapat melihat kode sumbernya melalui :::fungsi (yaitu stats:::t.ts), atau dengan menggunakan getAnywhere(). getAnywhere()berguna karena Anda tidak perlu tahu dari paket mana fungsi itu berasal.

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

Sistem pengiriman metode S4

Sistem S4 adalah sistem pengiriman metode yang lebih baru dan merupakan alternatif dari sistem S3. Berikut adalah contoh fungsi 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.

Outputnya sudah menawarkan banyak informasi. standardGenericadalah indikator fungsi S4. Metode untuk melihat metode S4 yang ditentukan ditawarkan dengan sangat membantu:

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

getMethod dapat digunakan untuk melihat kode sumber dari salah satu metode:

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

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

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

Ada juga metode dengan tanda tangan yang lebih kompleks untuk setiap metode, misalnya

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"

Untuk melihat kode sumber untuk salah satu metode ini seluruh tanda tangan harus disediakan, mis

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

Tidak cukup untuk memasok sebagian tanda tangan

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

Fungsi yang memanggil fungsi yang tidak diekspor

Dalam kasus ts.union, .cbindtsdan .makeNamesTsmerupakan fungsi yang tidak diekspor dari statsnamespace. Anda dapat melihat kode sumber fungsi yang tidak diekspor dengan menggunakan :::operator atau 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>

Fungsi yang memanggil kode yang dikompilasi

Perhatikan bahwa "dikompilasi" tidak merujuk ke kode R byte-dikompilasi seperti yang dibuat oleh paket kompiler . The <bytecode: 0x294e410>baris dalam output di atas menunjukkan bahwa fungsi ini byte-dikompilasi, dan Anda masih dapat melihat sumber dari baris perintah R.

Fungsi panggilan .C, .Call, .Fortran, .External, .Internal, atau .Primitivemenelepon titik masuk dalam kode dikompilasi, sehingga Anda akan harus melihat sumber kode dikompilasi jika Anda ingin memahami fungsi. Ini cermin GitHub dari kode sumber R adalah tempat yang layak untuk memulai. Fungsi ini pryr::show_c_sourcebisa menjadi alat yang berguna karena akan membawa Anda langsung ke halaman GitHub untuk .Internaldan .Primitivemenelepon. Paket dapat menggunakan .C, .Call, .Fortran, dan .External; tetapi tidak .Internalatau .Primitive, karena ini digunakan untuk memanggil fungsi-fungsi yang dibangun ke dalam interpreter R.

Panggilan ke beberapa fungsi di atas dapat menggunakan objek, bukan string karakter untuk referensi fungsi yang dikompilasi. Dalam kasus tersebut, objek adalah kelas "NativeSymbolInfo", "RegisteredNativeSymbol"atau "NativeSymbol"; dan mencetak objek menghasilkan informasi yang bermanfaat. Misalnya, optimpanggilan .External2(C_optimhess, res$par, fn1, gr1, con)(perhatikan itu C_optimhess, bukan "C_optimhess"). optimada dalam paket statistik, jadi Anda bisa mengetik stats:::C_optimhessuntuk melihat informasi tentang fungsi yang dikompilasi dipanggil.

Kode yang dikompilasi dalam sebuah paket

Jika Anda ingin melihat kode yang dikompilasi dalam sebuah paket, Anda perlu mengunduh / membongkar sumber paket. Binari yang diinstal tidak cukup. Kode sumber suatu paket tersedia dari repositori CRAN yang sama (atau yang kompatibel dengan CRAN) tempat asal paket tersebut. The download.packages()Fungsi bisa mendapatkan sumber paket untuk Anda.

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

Ini akan mengunduh versi sumber paket Matrix dan menyimpan .tar.gzfile yang sesuai di direktori saat ini. Kode sumber untuk fungsi-fungsi yang dikompilasi dapat ditemukan di srcdirektori file yang tidak dikompresi dan tidak dikompilasi. Langkah uncompressing dan unfaring dapat dilakukan di luar R, atau dari dalam Rmenggunakan untar()fungsi. Dimungkinkan untuk menggabungkan langkah unduhan dan perluasan menjadi satu panggilan (perhatikan bahwa hanya satu paket pada satu waktu yang dapat diunduh dan dibuka dengan cara ini):

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

Atau, jika pengembangan paket di-host secara publik (misalnya melalui GitHub , R-Forge , atau RForge.net ), Anda mungkin dapat menelusuri kode sumber online.

Kode yang dikompilasi dalam paket dasar

Paket tertentu dianggap paket "basis". Paket-paket ini kapal dengan R dan versi mereka dikunci ke versi R. Contohnya termasuk base, compiler, stats, dan utils. Dengan demikian, mereka tidak tersedia sebagai paket yang dapat diunduh terpisah pada CRAN seperti dijelaskan di atas. Sebaliknya, mereka adalah bagian dari pohon sumber R dalam direktori paket individu di bawah /src/library/. Cara mengakses sumber R dijelaskan di bagian selanjutnya.

Kode yang dikompilasi dibangun ke dalam interpreter R

Jika Anda ingin melihat kode bawaan ke interpreter R, Anda perlu mengunduh / membongkar sumber R; atau Anda dapat melihat sumber secara online melalui repositori R Subversion atau mirror github Winston Chang .

Artikel berita Uwe Ligges (PDF) (hlm. 43) adalah referensi umum yang baik tentang cara melihat kode sumber .Internaldan .Primitivefungsinya. Langkah-langkah dasar adalah pertama-tama mencari nama fungsi di src/main/names.cdan kemudian mencari nama "entri-C" dalam file di src/main/*.

Joshua Ulrich
sumber
71
Jika Anda menggunakan RStudio, itu akan berusaha untuk menarik sumber untuk fungsi kursor teks Anda selesai jika Anda menekan F2tombol.
Ari B. Friedman
1
@Ari B. Friedman Maaf atas pertanyaan terlambat ini. Apakah RStudio juga akan menarik kode sumber C untuk fungsi atau hanya untuk fungsi yang ditulis dalam R? Terima kasih
Cerah
3
@ Samir Saya percaya itu hanya sumber R.
Ari B. Friedman
@ AriB.Friedman - terima kasih Ari, ini berguna. Dalam kasus saya, saya masih membutuhkan pengetahuan yang ditunjukkan dalam jawaban ( scale, yaitu S3 - saya dapatkan UseMethod("scale")dan kemudian digunakan getAnywhere(scale.default)). Tapi fungsi polos bekerja dengan baik.
Tomasz Gandor
2
Imitasi adalah bentuk sanjungan yang tulus. Saya menganggap jawaban ini / wiki yang lebih dulu :) Sebelum ini rfaqs.com/source-code-of-r-method
JimLohse
94

Selain jawaban lain pada pertanyaan ini dan duplikatnya, berikut ini adalah cara yang baik untuk mendapatkan kode sumber untuk fungsi paket tanpa perlu mengetahui paket yang ada di dalamnya. Misalnya jika kita menginginkan sumber untuk randomForest::rfcv():

Untuk melihat / mengeditnya di jendela sembulan:

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

Untuk mengalihkan ke file terpisah :

capture.output(getAnywhere('rfcv'), file='source_rfcv.r')
smci
sumber
Diakui, getAnywhere adalah pilihan nama aneh untuk sesuatu yang seharusnya disebut findOnSearchPath atau sejenisnya.
smci
1
Saya akan mengangkat jawaban ini karena itu membuat saya dekat dengan apa yang saya inginkan. Apa yang sebenarnya saya inginkan, di RStudio, adalah View(foo); di mana foofungsi dari paket yang sudah dimuat.
Sigfried
1
@ Sigfried: edit()membuka editor teks (pilihan pengguna) , sedangkan View()membuka penampil spreadsheet tipe-Excel untuk data , yang terakhir baik untuk menelusuri data (multi-kolom), tetapi biasanya mengerikan untuk kode apa pun selain panjang mainan. Misalnya seperti yang saya mengisyaratkan, umumnya hal pertama yang ingin saya lakukan saat browsing fungsi adalah melewatkan / runtuhnya / boneka keluar semua arg-parsing dan default-tindakan logika, untuk melihat apa fungsi sebenarnya tidak .
smci
25

Itu terungkap ketika Anda debug menggunakan fungsi debug (). Misalkan Anda ingin melihat kode yang mendasari dalam fungsi t () transpos. Hanya mengetik 't', tidak banyak mengungkapkan.

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

Tapi, Menggunakan 'debug (functionName)', ia mengungkapkan kode yang mendasarinya, tanpa internal.

> 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 () menyelesaikan hal yang sama tanpa harus menggunakan undebug ()

Selva
sumber
Kelemahan dari metode ini dibandingkan dengan yang diberikan dalam jawaban yang diterima adalah bahwa Anda memerlukan pemanggilan fungsi kerja (semua parameter yang diperlukan ditentukan, dapat diterima); dan bahwa, di samping blok kode awal, Anda juga mendapatkan setiap blok pada saat dijalankan. Ini bagus untuk debugging, tetapi tidak optimal untuk mendapatkan sumbernya saja.
Brian Diggs
Ya, itu tidak optimal. Tetapi jika Anda pintar, Anda bisa mendapatkan sumbernya dengan cepat dan kotor, terutama untuk fungsi bawaan.
Selva
2
Saya juga merekomendasikan menggunakan debugoncedaripada debugdalam hal ini.
Joshua Ulrich
20

Untuk fungsi non-primitif, basis R mencakup fungsi yang disebut body()mengembalikan fungsi tubuh. Misalnya sumber print.Date()fungsi dapat dilihat:

body(print.Date)

akan menghasilkan ini:

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

Jika Anda bekerja dalam skrip dan ingin kode fungsi sebagai vektor karakter, Anda bisa mendapatkannya.

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

akan membuat Anda:

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

Mengapa saya ingin melakukan hal seperti itu? Saya sedang membuat objek S3 khusus ( x, di mana class(x) = "foo") berdasarkan daftar. Salah satu anggota daftar (bernama "kesenangan") adalah fungsi dan saya ingin print.foo()menampilkan kode sumber fungsi, indentasi. Jadi saya berakhir dengan cuplikan berikut di print.foo():

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

yang indentasi dan menampilkan kode yang terkait dengan x[["fun"]].

Geoffrey Poole
sumber
18

Tidak melihat bagaimana ini cocok dengan aliran jawaban utama tetapi itu membuat saya bingung untuk sementara waktu jadi saya menambahkannya di sini:

Operator Infix

Untuk melihat kode sumber dari beberapa operator dasar infix (misalnya, %%, %*%, %in%), penggunaan getAnywhere, misalnya:

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

Jawaban utamanya mencakup bagaimana cara menggunakan mirror untuk menggali lebih dalam.

MichaelChirico
sumber
6
Jawaban smci direkomendasikan getAnywhere. Atau Anda bisa menggunakan tanda kutip mundur jika Anda sudah tahu nama operator: `%in%`.
Joshua Ulrich
3
@JoshuaUlrich tidak tahu Anda bisa menggunakan backticks! Terima kasih. getAnywheredisebutkan dalam jawaban Anda juga, tetapi saya pikir referensi khusus untuk infix berguna untuk referensi di masa mendatang untuk jawaban ini - saya telah membaca halaman ini berkali-kali dan masih agak bingung mencoba menemukan kode untuk fungsi-fungsi tersebut untuk suatu sementara - dan saya tidak berpikir itu cocok dengan aliran jawaban lain (yang keduanya menggunakan getAnywhereuntuk tujuan lain).
MichaelChirico
10

Ada fungsi yang sangat berguna di R edit

new_optim <- edit(optim)

Ini akan membuka kode sumber optimmenggunakan editor yang ditentukan dalam R's options, dan kemudian Anda dapat mengeditnya dan menetapkan fungsi yang dimodifikasi new_optim. Saya sangat menyukai fungsi ini untuk melihat kode atau men-debug kode, misalnya, mencetak beberapa pesan atau variabel atau bahkan menetapkannya ke variabel global untuk penyelidikan lebih lanjut (tentu saja Anda dapat menggunakan debug).

Jika Anda hanya ingin melihat kode sumber dan tidak ingin kode sumber panjang yang mengganggu dicetak di konsol Anda, Anda dapat menggunakannya

invisible(edit(optim))

Jelas, ini tidak dapat digunakan untuk melihat kode sumber C / C ++ atau Fortran.

BTW, editdapat membuka objek lain seperti daftar, matriks, dll, yang kemudian menunjukkan struktur data dengan atribut juga. Fungsi dedapat digunakan untuk membuka editor excel like (jika GUI mendukungnya) untuk memodifikasi matriks atau bingkai data dan mengembalikan yang baru. Ini kadang-kadang berguna, tetapi harus dihindari dalam kasus biasa, terutama ketika matriks Anda besar.

Eric
sumber
3
Pendekatan ini hanya memunculkan sumber fungsi yang sama yang mencetak fungsi berikan (yaitu, sama seperti dalam pertanyaan). Lebih jauh / lebih dalam dari itu adalah tentang pertanyaan ini.
Brian Diggs
2
@BrianDiggs Ya, Anda benar. Saya tidak bermaksud memberikan jawaban atas pertanyaan itu, karena Yosua telah memberikan jawaban yang cukup lengkap. Saya hanya mencoba menambahkan sesuatu yang berkaitan dengan topik, menarik dan semoga bermanfaat untuk diketahui.
Eric
8

Selama fungsi ini ditulis dalam R murni bukan C / C ++ / Fortran, orang dapat menggunakan yang berikut ini. Kalau tidak, cara terbaik adalah debugging dan menggunakan " lompat ke ":

> functionBody(functionName)
KIA
sumber
2
Ini sama dengan body. identical(functionBody, body)adalah TRUE.
Joshua Ulrich
1
base::bodydan methods::functionBody, meskipun mereka tidak suka dihilangkan. bodybisa ditimpa juga: rdocumentation.org/search?q=body
Moody_Mudskipper
7

Di RStudio, ada (setidaknya) 3 cara:

  1. Tekan tombol F2 saat kursor berada pada fungsi apa pun.
  2. Klik pada nama fungsi sambil menahan Ctrl atau Command
  3. View(function_name) (seperti yang dinyatakan di atas)

Panel baru akan terbuka dengan kode sumber. Jika Anda mencapai .Primitive atau .C Anda akan memerlukan metode lain, maaf.

Arthur Yip
sumber
5

View([function_name])- mis. View(mean)Pastikan untuk menggunakan huruf besar [V]. Kode baca-saja akan terbuka di editor.

Koo
sumber
5

Anda juga dapat mencoba menggunakan print.function(), yang merupakan S3 generik, untuk mendapatkan fungsi tulis di konsol.

strboul
sumber
3
print.function()adalah metode S3 . Generiknya adalah print(). Dan umumnya bukan ide yang baik untuk memanggil metode secara langsung. Itu mengalahkan seluruh tujuan fungsi generik dan metode pengiriman.
Joshua Ulrich