Bir işlevin kaynak kodunu nasıl görüntüleyebilirim?


551

Nasıl çalıştığını görmek için bir işlev için kaynak koduna bakmak istiyorum. Bilgi istemine adını yazarak bir işlevi yazdırabileceğimi biliyorum:

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

Bu durumda ne anlama UseMethod("t")geliyor? Aslında tarafından kullanılan kaynak kodunu nasıl bulabilirim, örneğin:t(1:10) ?

Ben gördüğümde arasında bir fark var mı UseMethodve ben gördüğümde standardGenericve showMethodsolduğu gibi, 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.

Diğer durumlarda, R işlevlerinin çağrıldığını görebiliyorum, ancak bu işlevlerin kaynak kodunu bulamıyorum.

> 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

Ben gibi işlevleri bulurum nasıl .cbindtsve.makeNamesTs ?

Yine de diğer durumlarda, biraz R kodu var, ancak işlerin çoğu başka bir yerde yapılıyor gibi görünüyor.

> 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İşlevin ne yaptığını nasıl öğrenebilirim ? Benzer şekilde bazı fonksiyonlar diyoruz .C, .Call, .Fortran, .External, veya .Internal. Bunlar için kaynak kodunu nasıl bulabilirim?




Yanıtlar:


518

UseMethod("t")size farklı nesne sınıfları için yöntemler t()içeren ( S3 ) genel bir işlev olduğunu söylüyor .

S3 yöntemi sevk sistemi

S3 sınıfları için, methodsişlevi, belirli bir genel işlev veya sınıf için yöntemleri listelemek üzere kullanabilirsiniz .

> 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

"Görünmeyen işlevler yıldız işaretlidir", işlevin paketinin ad alanından dışa aktarılmadığı anlamına gelir. Kaynak kodunu :::işlev (örn. stats:::t.ts) Üzerinden veya düğmesini kullanarak görüntüleyebilirsiniz getAnywhere(). getAnywhere()yararlıdır çünkü işlevin hangi paketten geldiğini bilmek zorunda değilsiniz.

> 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 yöntemi sevk sistemi

S4 sistemi daha yeni bir yöntem dağıtım sistemidir ve S3 sistemine bir alternatiftir. İşte bir S4 işlevi örneği:

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

Çıktı zaten çok fazla bilgi sunuyor. standardGenericbir S4 fonksiyonunun göstergesidir. Tanımlanmış S4 yöntemlerini görme yöntemi faydalı bir şekilde sunulmaktadır:

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

getMethod yöntemlerden birinin kaynak kodunu görmek için kullanılabilir:

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

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

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

Ayrıca, her yöntem için daha karmaşık imzalara sahip yöntemler de vardır, örneğin

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"

Bu yöntemlerden birinin kaynak kodunu görmek için imzanın tamamı sağlanmalıdır, örn.

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

Kısmi imza sağlamak yeterli olmayacaktır

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

Dışa aktarılmamış işlevleri çağıran işlevler

Durumunda ts.union, .cbindtsve .makeNamesTsgelen unexported fonksiyonlardır statsad. :::Operatörü veya kullanarak dışa aktarılmayan fonksiyonların kaynak kodunu görüntüleyebilirsiniz 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>

Derlenmiş kodu çağıran işlevler

"Derlenmiş", derleyici paketi tarafından oluşturulan bayt-derlenmiş R kodu anlamına gelmez . <bytecode: 0x294e410>Yukarıdaki çıkış çizgisi bayt derlenmiş olduğunu gösterir ve hala R komut hattı kaynağını görüntüleyebilir.

Fonksiyonlar bu çağrı .C, .Call, .Fortran, .External, .Internal, veya .Primitivetam olarak işlevini anlamak istiyorsanız derlenmiş kod kaynaklarına bakmak zorunda kalacak, böylece derlenmiş kod giriş noktalarını çağrıda bulunuyorlar. R kaynak kodunun bu GitHub aynası, başlamak için iyi bir yerdir. İşlev pryr::show_c_source, sizi doğrudan GitHub sayfasına yönlendirecek .Internalve .Primitivearayacak faydalı bir araç olabilir . Paketler kullanabilir .C, .Call, .Fortranve .External; ancak değil .Internalveya .Primitive, çünkü bunlar R yorumlayıcısında yerleşik işlevleri çağırmak için kullanılır.

Yukarıdaki işlevlerden bazılarına yapılan çağrılar, derlenmiş işleve başvurmak için karakter dizesi yerine bir nesne kullanabilir. Bu gibi durumlarda, nesne sınıfının olduğu "NativeSymbolInfo", "RegisteredNativeSymbol"ya da "NativeSymbol"; ve nesnenin basılması yararlı bilgiler verir. Örneğin, optimaramalar .External2(C_optimhess, res$par, fn1, gr1, con)(not edin C_optimhess, değil "C_optimhess"). optimistatistik paketinde olduğundan yazabilirsinizstats:::C_optimhess çağrılan derlenmiş işlev hakkındaki bilgileri görmek .

Bir pakette derlenmiş kod

Derlenmiş kodu bir pakette görüntülemek istiyorsanız, paket kaynağını indirmeniz / paketinden çıkarmanız gerekir. Kurulu ikili dosyalar yeterli değildir. Bir paketin kaynak kodu, paketin orijinal olarak kurulduğu aynı CRAN (veya CRAN uyumlu) deposundan edinilebilir. download.packages()Fonksiyon sizin için paket kaynağını alabilirsiniz.

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

Bu, Matrix paketinin kaynak sürümünü indirir ve ilgili .tar.gzdosyayı geçerli dizine kaydeder. Derlenmiş işlevlerin kaynak kodu src, sıkıştırılmamış ve yıldızlanmamış dosyanın dizininde bulunabilir . Sıkıştırma ve açma adımı adımı, işlevin dışında Rveya içinden Ryapılabilir untar(). İndirme ve genişletme adımını tek bir çağrıda birleştirmek mümkündür (bir seferde yalnızca bir paketin bu şekilde indirilip paketlenebileceğini unutmayın):

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

Alternatif olarak, paket geliştirme herkese açık olarak barındırılıyorsa (örneğin GitHub , R-Forge veya RForge.net aracılığıyla ), kaynak koduna çevrimiçi olarak göz atabilirsiniz.

Temel pakette derlenmiş kod

Bazı paketler "temel" paketler olarak kabul edilir. Bu paketler R ile birlikte gelen ve bunların sürüm içerir R. Örnek sürümüne kilitlenir base, compiler, statsve utils. Bu itibarla, yukarıda açıklandığı gibi CRAN üzerinde ayrı indirilebilir paketler halinde mevcut değildirler. Aksine, altında ayrı paket dizinlerindeki R kaynak ağacının bir parçasıdırlar /src/library/. R kaynağına nasıl erişileceği bir sonraki bölümde açıklanmaktadır.

R yorumlayıcısında derlenmiş kod

R yorumlayıcısında yerleşik olan kodu görüntülemek istiyorsanız, R kaynaklarını indirmeniz / paketinden çıkarmanız gerekir; veya kaynakları R Subversion deposu veya Winston Chang'ın github aynası aracılığıyla çevrimiçi olarak görüntüleyebilirsiniz .

Uwe Ligges'in R haber makalesi (PDF) (s. 43), kaynak kodunun .Internalve .Primitivefonksiyonlarının nasıl görüntüleneceğine dair iyi bir genel referanstır . Temel adımlar önce işlev adını src/main/names.caramak ve daha sonra içindeki dosyalarda "C-giriş" adını aramaktır src/main/*.


71
Eğer kullanırsanız RStudio, bu basarsanız metin imleci bitti fonksiyonu için kaynak çekmeye çalışacaktır F2anahtarı.
Ari B. Friedman

1
@Ari B. Friedman Bu geç soru için özür dilerim. RStudio ayrıca fonksiyon için mi yoksa sadece R ile yazılmış fonksiyonlar için C kaynak kodunu mu çeker? Teşekkürler
Sunny

3
@Samir Bunun sadece R kaynağı olduğuna inanıyorum.
Ari B. Friedman

@ AriB.Friedman - teşekkürler Ari, bu kullanışlı. Benim durumumda hala cevapta gösterilen bilgiye ihtiyacım vardı ( scaleki bu S3 - aldım UseMethod("scale")ve kullandım getAnywhere(scale.default)). Ancak sade işlevler gayet iyi çalışıyor.
Tomasz Gandor

2
Taklit, bu cevabın / wiki'nin
JimLohse 27:18

94

Bu sorunun ve Yinelenenlerde diğer cevaplara ek olarak, burada İçinde hangi pakete bilmesine gerek kalmadan bir paket fonksiyonu için kaynak kodu almak için iyi bir yoldur mesela biz kaynağını istiyorum. randomForest::rfcv():

To / düzenleyebilir bir pop-up pencerede o:

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

İçin ayrı bir dosyaya yönlendirmek :

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

Kuşkusuz, getAnywhere , findOnSearchPath veya benzeri olarak adlandırılması gereken bir şey için başka bir tuhaf R adı seçimidir .
smci

1
Bu cevabı iptal edeceğim çünkü beni istediğim şeye yaklaştırdı. Aslında istediğim RStudio'da View(foo); Nerede foozaten yüklü paketinden bir işlev oldu.
Sigfried

1
@Sigfried: edit()bir metin düzenleyicisi açar (kullanıcının tercihi) , oysa verilerView() için Excel tipi bir elektronik tablo görüntüleyici açar , ikincisi verileri taramak için iyidir (çok sütunlu), ancak genellikle oyuncak uzunluğu dışındaki herhangi bir şeyin kodu için korkunçtur. Mesela ben genelde ben bir işlev gezinirken yapmak istediğim ilk şey / atlamak tüm arg-ayrıştırma ve varsayılan eylem mantığı dışarı çöküşü / kukla, fonksiyon aslında ne olduğunu görmek için ise, ima olarak yapar .
smci

25

Hata ayıklama () işlevini kullanarak hata ayıkladığınızda ortaya çıkar. T () transpoze fonksiyonunda temel alınan kodu görmek istediğinizi varsayalım. Sadece 't' yazmak fazla bir şey ifade etmiyor.

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

Ancak, 'debug (functionName)' kullanarak, temel kodu ortaya çıkarır, içleri sans eder.

> 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: undebug () kullanmak zorunda kalmadan debugonce () aynı şeyi yapar


Bu yöntemin kabul edilen cevapta verilenlerle karşılaştırıldığında dezavantajları, bir çalışma fonksiyonu çağrısına ihtiyacınız olmasıdır (tüm gerekli parametreler, kabul edilebilir şekilde belirtilmiştir); ve ilk kod bloğuna ek olarak, her bloğu çalıştırıldığı sırada da alırsınız. Bu hata ayıklama için harikadır, ancak sadece kaynağı elde etmek için uygun değildir.
Brian Diggs

Evet, optimal değil. Ancak akıllıysanız, yerleşik işlevler için esp'yi hızlı ve kirli alabilirsiniz.
Selva

2
Ayrıca bu örnek debugonceyerine kullanmanızı öneririm debug.
Joshua Ulrich

20

İlkel olmayan işlevler için, R bazı işlev body()gövdesini döndüren bir işlev içerir . Örneğin, print.Date()işlevin kaynağı görüntülenebilir:

body(print.Date)

bunu üretecek:

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

Bir komut dosyasında çalışıyorsanız ve işlev kodunu bir karakter vektörü olarak istiyorsanız, alabilirsiniz.

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

Seni alacak:

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

Neden böyle bir şey yapmak isteyeyim ki? Bir listeye göre özel bir S3 nesnesi ( xnerede class(x) = "foo") oluşturuyordum. Liste üyelerinden biri ("fun" olarak adlandırılır) bir işlevdi ve print.foo()girintili işlev kaynak kodunu görüntülemek istedim . Bu yüzden şu snippet'i buldum print.foo():

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

ile ilişkili kodu girintiler ve görüntüler x[["fun"]].


18

Bunun ana cevabın akışına nasıl uyduğunu görmedim ama bir süre beni zorladı, bu yüzden buraya ekliyorum:

Infix Operatörleri

Bazı taban infix operatörleri (örneğin, kaynak kodunu görmek için %%, %*%, %in%), kullanım getAnywhere, örneğin:

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

Ana cevap, daha derin kazmak için aynaların nasıl kullanılacağını kapsar.


6
smci'nin cevabı önerilir getAnywhere. Zaten operatörün adını biliyorsanız Ya da sadece komutu ters tırnak kullanabilirsiniz: `%in%`.
Joshua Ulrich

3
@JoshuaUlrich backticks kullanabileceğinizi bilmiyordu! Teşekkürler. getAnywherecevabınızda da belirtiliyor, ancak infix'e özel bir referansın bu cevaba gelecekteki referans için yararlı olduğunu düşünüyorum - Bu sayfayı defalarca okudum ve hala bu tür işlevler için kod bulmaya çalışırken biraz şaşırdım süre - ve ben başka bir cevap (her ikisi de getAnywherebaşka bir amaçla kullanıyor ) akışına uygun olduğunu düşünmüyordu .
MichaelChirico

10

R'de çok kullanışlı bir fonksiyon var edit

new_optim <- edit(optim)

optimR'lerde belirtilen düzenleyiciyi kullanarak kaynak kodunu açacak optionsve daha sonra düzenleyebilir ve değiştirilen işlevi atayabilirsiniz new_optim. Bu işlevi, kodu görüntülemek veya kodda hata ayıklamak için çok seviyorum, örneğin, bazı mesajları veya değişkenleri yazdırın veya hatta daha fazla araştırma için küresel değişkenlere atayın (elbette kullanabilirsiniz debug).

Yalnızca kaynak kodunu görüntülemek istiyorsanız ve konsolunuzda can sıkıcı uzun kaynak kodunun yazdırılmasını istemiyorsanız,

invisible(edit(optim))

Açıkçası, bu C / C ++ veya Fortran kaynak kodunu görüntülemek için kullanılamaz.

BTW, editdaha sonra veri yapısını özniteliklerle gösteren list, matrix vb. Gibi diğer nesneleri de açabilir. Fonksiyon de, matrisi veya veri çerçevesini değiştirmek ve yenisini döndürmek için düzenleyici gibi bir excel açmak için kullanılabilir (GUI destekliyorsa). Bu bazen kullanışlıdır, ancak özellikle matris büyük olduğunda olağan durumda kaçınılmalıdır.


3
Bu yaklaşım yalnızca işlevin yazdırılmasıyla aynı işlev kaynağını getirir (yani, sorudakiyle aynıdır). Bundan daha fazla / derinleşmek bu sorunun ne olduğuyla ilgilidir.
Brian Diggs

2
@BrianDiggs Evet, haklısın. Soruya cevap vermek istemedim, çünkü Joshua oldukça eksiksiz bir cevap verdi. Sadece konuyla ilgili bir şeyler eklemeye çalışıyorum, ilginç ve bilmek yararlı olabilir.
Eric

8

Fonksiyon C / C ++ / Fortran değil saf R ile yazıldığı sürece, aşağıdakiler kullanılabilir. Aksi takdirde en iyi yol hata ayıklamak ve " içine atlamak " kullanmaktır:

> functionBody(functionName)

2
Bu ile aynı body. identical(functionBody, body)olduğunu TRUE.
Joshua Ulrich

1
base::bodyve methods::functionBodyayrılmaları pek mümkün olmasa da. bodyda geçersiz kılınabilir: rdocumentation.org/search?q=body
Moody_Mudskipper

7

RStudio'da (en az) 3 yol vardır:

  1. İmleç herhangi bir işlev üzerindeyken F2 tuşuna basın.
  2. Ctrl veya Command tuşunu basılı tutarken işlev adına tıklayın
  3. View(işlev_adı) (yukarıda belirtildiği gibi)

Kaynak koduyla birlikte yeni bir bölme açılır. .Primitive veya .C'ye ulaşırsanız, başka bir yönteme ihtiyacınız olacak, üzgünüm.


5

View([function_name])- Örneğin. View(mean)Büyük harf [V] kullandığınızdan emin olun. Düzenleyicide salt okunur kod açılacaktır.


5

print.function()Konsolda yazma işlevini elde etmek için S3 genel olan kullanmayı da deneyebilirsiniz .


3
print.function()bir S3 yöntemidir . Jenerik print(). Ve yöntemleri doğrudan çağırmak genellikle iyi bir fikir değildir. Bu, genel işlevlerin ve yöntem dağıtımının tüm amacını yener.
Joshua Ulrich
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.