Jak mogę wyświetlić kod źródłowy funkcji?


550

Chcę spojrzeć na kod źródłowy funkcji, aby zobaczyć, jak ona działa. Wiem, że mogę wydrukować funkcję, wpisując jej nazwę w wierszu polecenia:

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

Co w tym przypadku UseMethod("t")oznacza? Jak znaleźć kod źródłowy, z którego faktycznie korzysta, na przykład t(1:10):?

Czy jest jakaś różnica między tym, kiedy widzę, UseMethoda kiedy widzę standardGenerici showMethods, jak w przypadku 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.

W innych przypadkach widzę, że wywoływane są funkcje R, ale nie mogę znaleźć kodu źródłowego dla tych funkcji.

> 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

Jak znaleźć funkcje takie jak .cbindtsi .makeNamesTs?

W jeszcze innych przypadkach jest trochę kodu R, ale większość pracy wydaje się być wykonywana gdzie indziej.

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

Jak dowiedzieć się, na czym .Primitivepolega ta funkcja? Podobnie, niektóre funkcje zadzwonić .C, .Call, .Fortran, .External, lub .Internal. Jak znaleźć dla nich kod źródłowy?




Odpowiedzi:


518

UseMethod("t")mówi ci, że t()jest to ogólna funkcja ( S3 ), która ma metody dla różnych klas obiektów.

System wysyłki metodą S3

W przypadku klas S3 można użyć methodsfunkcji, aby wyświetlić listę metod dla określonej funkcji ogólnej lub klasy.

> 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

„Niewidoczne funkcje są oznaczone gwiazdką” oznacza, że ​​funkcja nie jest eksportowana z przestrzeni nazw swojego pakietu. Nadal możesz przeglądać jego kod źródłowy za pomocą :::funkcji (tj. stats:::t.ts) Lub za pomocą getAnywhere(). getAnywhere()jest przydatne, ponieważ nie musisz wiedzieć, z którego pakietu pochodzi ta funkcja.

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

System wysyłki metody S4

System S4 to nowszy system dyspozytorski i stanowi alternatywę dla systemu S3. Oto przykład funkcji 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.

Dane wyjściowe już oferują wiele informacji. standardGenericjest wskaźnikiem funkcji S4. Pomocna jest metoda wyświetlania zdefiniowanych metod S4:

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

getMethod można użyć do wyświetlenia kodu źródłowego jednej z metod:

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

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

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

Istnieją również metody z bardziej złożonymi podpisami dla każdej metody, na przykład

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"

Aby zobaczyć kod źródłowy jednej z tych metod, należy podać cały podpis, np

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

Podanie częściowego podpisu nie wystarczy

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

Funkcje wywołujące funkcje nieobsługiwane

W przypadku ts.union, .cbindtsi .makeNamesTssą unexported funkcje z statsprzestrzeni nazw. Możesz wyświetlić kod źródłowy niewyportowanych funkcji za pomocą :::operatora lub 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>

Funkcje wywołujące skompilowany kod

Zauważ, że „skompilowany” nie odnosi się do skompilowanego bajtowo kodu R utworzonego przez pakiet kompilatora . <bytecode: 0x294e410>Linia w powyższym wyjściu wskazuje, że funkcja jest bajt skompilowany, i nadal można zobaczyć źródło z linii poleceń R.

Funkcje połączeń .C, .Call, .Fortran, .External, .Internal, lub .Primitivedzwonisz punkty wejścia w skompilowanego kodu, więc trzeba będzie spojrzeć na źródła skompilowanego kodu, jeśli chcesz, aby w pełni zrozumieć tę funkcję. To lustro GitHub kodu źródłowego R jest przyzwoitym miejscem do rozpoczęcia. Ta funkcja pryr::show_c_sourcemoże być przydatnym narzędziem, ponieważ zabierze Cię bezpośrednio do strony GitHub .Internali .Primitivewywołań. Pakiety mogą korzystać .C, .Call, .Fortran, i .External; ale nie .Internallub .Primitive, ponieważ są one używane do wywoływania funkcji wbudowanych w interpreter R.

Wywołania niektórych z powyższych funkcji mogą wykorzystywać obiekt zamiast ciągu znaków w celu odniesienia do skompilowanej funkcji. W tych przypadkach, obiekt jest klasy "NativeSymbolInfo", "RegisteredNativeSymbol"lub "NativeSymbol"; a wydrukowanie obiektu dostarcza użytecznych informacji. Na przykład optimpołączenia .External2(C_optimhess, res$par, fn1, gr1, con)(uwaga, że C_optimhessnie "C_optimhess"). optimznajduje się w pakiecie statystyk, więc możesz wpisać, stats:::C_optimhessaby wyświetlić informacje o wywołanej kompilowanej funkcji.

Skompilowany kod w pakiecie

Jeśli chcesz wyświetlić skompilowany kod w pakiecie, musisz pobrać / rozpakować źródło pakietu. Zainstalowane pliki binarne nie są wystarczające. Kod źródłowy pakietu jest dostępny z tego samego repozytorium CRAN (lub zgodnego z CRAN), z którego pakiet został pierwotnie zainstalowany. download.packages()Funkcja może dostać źródła pakietów dla Ciebie.

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

Spowoduje to pobranie źródłowej wersji pakietu Matrix i zapisanie odpowiedniego .tar.gzpliku w bieżącym katalogu. Kod źródłowy skompilowanych funkcji można znaleźć w srckatalogu nieskompresowanego i nieopartego pliku. Etap rozpakowywania i odznaczania można wykonać poza funkcją Rlub od wewnątrz Rza pomocą tej untar()funkcji. Możliwe jest połączenie kroku pobierania i rozszerzenia w jedno wywołanie (pamiętaj, że tylko jeden pakiet na raz można pobrać i rozpakować w ten sposób):

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

Alternatywnie, jeśli tworzenie pakietu jest hostowane publicznie (np. Przez GitHub , R-Forge lub RForge.net ), prawdopodobnie możesz przeglądać kod źródłowy online.

Skompilowany kod w pakiecie podstawowym

Niektóre pakiety są uważane za pakiety „podstawowe”. Te pakiety dostarczane z R i ich wersja jest zablokowane do wersji R. Przykłady obejmują base, compiler, stats, i utils. Jako takie nie są dostępne jako osobne pakiety do pobrania w CRAN, jak opisano powyżej. Są raczej częścią drzewa źródłowego R w poszczególnych katalogach pakietów w /src/library/. Sposób uzyskania dostępu do źródła R opisano w następnej sekcji.

Skompilowany kod wbudowany w interpreter R.

Jeśli chcesz wyświetlić kod wbudowany w interpreter R, musisz pobrać / rozpakować źródła R; lub możesz przeglądać źródła online za pośrednictwem repozytorium R Subversion lub lustra github Winstona Changa .

Artykuł informacyjny R Uwe Liggesa (PDF) (s. 43) stanowi dobre ogólne odniesienie do tego, jak wyświetlić kod źródłowy .Internali .Primitivefunkcje. Podstawowe kroki to najpierw poszukać nazwy funkcji w, src/main/names.ca następnie poszukać nazwy „C-entry” w plikach w src/main/*.


71
Jeśli go użyjesz RStudio, po naciśnięciu F2klawisza spróbujesz pobrać źródło funkcji, na której kończy się kursor tekstowy .
Ari B. Friedman

1
@Ari B. Friedman Przepraszamy za to późne pytanie. Czy RStudio pobierze również kod źródłowy C dla funkcji, czy tylko dla funkcji napisanych w R? Dzięki
Sunny

3
@Samir Wierzę, że to tylko źródło R.
Ari B. Friedman

@ AriB.Friedman - dzięki Ari, jest to przydatne. W moim przypadku nadal potrzebowałem wiedzy pokazanej w odpowiedzi ( scaleczyli S3 - dostałem UseMethod("scale")i wykorzystałem getAnywhere(scale.default)). Ale proste funkcje działają dobrze.
Tomasz Gandor

2
Imitacja jest najszczerszą formą pochlebstwa Zakładam, że ta odpowiedź / wiki była pierwsza :) Przed tym rfaqs.com/source-code-of-r-method
JimLohse

94

Oprócz innych odpowiedzi na to pytanie i jego duplikaty, oto dobry sposób na uzyskanie kodu źródłowego dla funkcji pakietu bez konieczności wiedzieć, w którym pakiecie się znajduje. Np. Jeśli chcemy źródła randomForest::rfcv():

Aby wyświetlić / edytować w wyskakującym oknie:

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

Aby przekierować do osobnego pliku :

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

Trzeba przyznać, że getAnywhere to kolejny zwariowany wybór nazwy R dla czegoś, co powinno być nazwane findOnSearchPath lub podobnym.
smci

1
Głosuję za odpowiedzią, ponieważ zbliżyło mnie to do tego, czego chciałem. W RStudio tak naprawdę chciałem View(foo); gdzie foobyła funkcja z już załadowanego pakietu.
Sigfried

1
@ Sigfried: edit()otwiera edytor tekstu (wybrany przez użytkownika) , podczas gdy View()otwiera przeglądarkę arkuszy kalkulacyjnych typu Excel dla danych , ta ostatnia jest dobra do przeglądania danych (wielokolumnowych), ale zwykle jest okropna dla kodu innego niż długość zabawki. Na przykład, jak wskazałem, ogólnie pierwszą rzeczą, którą chcę zrobić podczas przeglądania funkcji, jest pominięcie / zwinięcie / obojętne logiki parsowania i domyślnej akcji, aby zobaczyć, co ta funkcja faktycznie robi .
smci

25

Ujawnia się podczas debugowania za pomocą funkcji debug (). Załóżmy, że chcesz zobaczyć podstawowy kod w funkcji transpozycji t (). Samo wpisanie „t” nie ujawnia wiele.

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

Ale używając „debugowania (functionName)”, ujawnia on podstawowy kod, bez wewnętrznych elementów.

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

EDYCJA: debugonce () dokonuje tego samego bez konieczności używania undebug ()


Wadami tej metody w porównaniu do podanych w zaakceptowanej odpowiedzi jest to, że potrzebujesz działającego wywołania funkcji (wszystkie niezbędne parametry określone, akceptowalne); i że oprócz początkowego bloku kodu, każdy blok otrzymujesz w momencie jego uruchomienia. Jest to świetne do debugowania, ale nie jest optymalne do uzyskania źródła.
Brian Diggs,

Tak, to nie jest optymalne. Ale jeśli jesteś sprytny, możesz uzyskać źródło szybko i brudne, szczególnie dla wbudowanych funkcji.
Selva

2
Polecam również użycie debugoncezamiast debugw tym przypadku.
Joshua Ulrich

20

W przypadku funkcji niebędących prymitywnymi podstawa R zawiera funkcję o nazwie, body()która zwraca treść funkcji. Na przykład można wyświetlić źródło print.Date()funkcji:

body(print.Date)

wyprodukuje to:

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

Jeśli pracujesz w skrypcie i chcesz, aby kod funkcji był wektorem znaków, możesz go zdobyć.

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

dostanie cię:

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

Dlaczego miałbym chcieć zrobić coś takiego? Tworzyłem niestandardowy obiekt S3 ( x, gdzie class(x) = "foo") na podstawie listy. Jeden z członków listy (o nazwie „fun”) był funkcją i chciałem print.foo()wyświetlić kod źródłowy funkcji z wcięciem. Więc skończyłem z następującym fragmentem kodu w print.foo():

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

który wcina i wyświetla kod związany z x[["fun"]].


18

Nie widziałem, jak to pasuje do głównej odpowiedzi, ale przez chwilę mnie zaskoczyło, więc dodaję to tutaj:

Infix Operators

Aby zobaczyć kod źródłowy niektórych operatorów infiksowych bazowej (np %%, %*%, %in%), użyj getAnywhere, np:

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

Główna odpowiedź dotyczy tego, jak następnie używać luster do głębszego kopania.


6
Odpowiedź smci jest zalecane getAnywhere. Lub może po prostu użyć odwrócone, pojedyncze apostrofy, jeśli już znasz nazwę operatora: `%in%`.
Joshua Ulrich,

3
@JoshuaUlrich nie wiedział, że możesz używać backticks! Dzięki. getAnywherejest również wspomniany w twojej odpowiedzi, ale myślę, że konkretne odniesienie do infix jest przydatne do przyszłego odniesienia do tej odpowiedzi - czytałem tę stronę wiele razy i wciąż byłem nieco zakłopotany próbą znalezienia kodu dla takich funkcji dla podczas gdy - i nie sądzę, że pasuje do przepływu żadnej innej odpowiedzi (które są używane getAnywherew innym celu).
MichaelChirico,

10

W języku R jest bardzo przydatna funkcja edit

new_optim <- edit(optim)

Otworzy kod źródłowy optimkorzystania z edytora określonego w R options, a następnie możesz go edytować i przypisać zmodyfikowaną funkcję do new_optim. Bardzo podoba mi się ta funkcja do przeglądania kodu lub do debugowania kodu, np. Drukowania niektórych komunikatów lub zmiennych, a nawet przypisywania ich do zmiennych globalnych w celu dalszego badania (oczywiście można użyć debug).

Jeśli chcesz tylko wyświetlić kod źródłowy i nie chcesz, aby denerwujący długi kod źródłowy został wydrukowany na konsoli, możesz go użyć

invisible(edit(optim))

Oczywiście nie można tego użyć do przeglądania kodu źródłowego C / C ++ lub Fortran.

BTW, editmoże otwierać inne obiekty, takie jak lista, macierz itp., Które następnie pokazują strukturę danych z atrybutami. Funkcji demożna użyć do otwarcia edytora podobnego do programu Excel (jeśli GUI go obsługuje), aby zmodyfikować matrycę lub ramkę danych i zwrócić nowy. Jest to czasami przydatne, ale należy tego unikać w zwykłych przypadkach, szczególnie gdy macierz jest duża.


3
Podejście to wywołuje tylko to samo źródło funkcji, które daje wydruk funkcji (to samo, co w pytaniu). Coraz głębiej / głębiej niż o to chodzi w tym pytaniu.
Brian Diggs,

2
@BrianDiggs Tak, masz rację. Nie chciałem udzielić odpowiedzi na pytanie, ponieważ Joshua udzielił dość kompletnej odpowiedzi. Po prostu staram się dodać coś związanego z tematem, interesujące i może być przydatne.
Eric

8

Tak długo, jak funkcja jest napisana w czystym R, a nie w C / C ++ / Fortran, można użyć następujących. W przeciwnym razie najlepszym sposobem jest debugowanie i użycie polecenia „ skok do ”:

> functionBody(functionName)

2
To jest to samo co body. identical(functionBody, body)jest TRUE.
Joshua Ulrich

1
base::bodyi methods::functionBodychociaż raczej nie zostaną zdemaskowani. bodymożna też pominąć
Moody_Mudskipper

7

W RStudio istnieją (co najmniej) 3 sposoby:

  1. Naciśnij klawisz F2, gdy kursor znajduje się na dowolnej funkcji.
  2. Kliknij nazwę funkcji, przytrzymując klawisz Ctrl lub Command
  3. View(nazwa_funkcji) (jak podano powyżej)

Otworzy się nowe okienko z kodem źródłowym. Jeśli przejdziesz do .Primitive lub .C będziesz potrzebować innej metody, przepraszam.


5

View([function_name])- np. View(mean)Upewnij się, że używasz wielkich liter [V]. Kod tylko do odczytu zostanie otwarty w edytorze.


5

Możesz także spróbować użyć print.function()S3, aby uzyskać funkcję zapisu w konsoli.


3
print.function()jest metodą S3 . Ogólny to print(). I generalnie nie jest dobrym pomysłem wywoływać metody bezpośrednio. To pokonuje cały cel funkcji ogólnych i metody wysyłania.
Joshua Ulrich
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.