Come posso visualizzare il codice sorgente di una funzione?


551

Voglio vedere il codice sorgente di una funzione per vedere come funziona. So di poter stampare una funzione digitandone il nome al prompt:

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

In questo caso, cosa UseMethod("t")significa? Come posso trovare il codice sorgente effettivamente utilizzato da, ad esempio t(1:10):?

C'è una differenza tra quando vedo UseMethode quando vedo standardGenerice showMethods, come con 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.

In altri casi, posso vedere che vengono chiamate le funzioni R, ma non riesco a trovare il codice sorgente per tali funzioni.

> 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

Come trovo funzioni come .cbindtse .makeNamesTs?

In altri casi, c'è un po 'di codice R, ma la maggior parte del lavoro sembra essere fatta da qualche altra parte.

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

Come faccio a sapere cosa fa la .Primitivefunzione? Analogamente, alcune funzioni di chiamata .C, .Call, .Fortran, .External, o .Internal. Come posso trovare il codice sorgente per quelli?




Risposte:


518

UseMethod("t")ti sta dicendo che t()è una funzione generica ( S3 ) che ha metodi per diverse classi di oggetti.

Il sistema di spedizione del metodo S3

Per le classi S3, è possibile utilizzare la methodsfunzione per elencare i metodi per una particolare funzione o classe generica.

> 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

"Le funzioni non visibili sono contrassegnate da un asterisco" significa che la funzione non viene esportata dallo spazio dei nomi del suo pacchetto. Puoi ancora visualizzare il suo codice sorgente tramite la :::funzione (es. stats:::t.ts), O usando getAnywhere(). getAnywhere()è utile perché non devi sapere da quale pacchetto proviene la funzione.

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

Il sistema di spedizione del metodo S4

Il sistema S4 è un sistema di invio del metodo più recente ed è un'alternativa al sistema S3. Ecco un esempio di una funzione 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.

L'output offre già molte informazioni. standardGenericè un indicatore di una funzione S4. Il metodo per vedere i metodi S4 definiti è offerto utile:

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

getMethod può essere usato per vedere il codice sorgente di uno dei metodi:

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

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

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

Esistono anche metodi con firme più complesse per ciascun metodo, ad esempio

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"

Per visualizzare il codice sorgente di uno di questi metodi, è necessario fornire l'intera firma, ad es

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

Non sarà sufficiente fornire la firma parziale

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

Funzioni che chiamano funzioni non esportate

Nel caso di ts.union, .cbindtse .makeNamesTssono funzioni non esportate dallo statsspazio dei nomi. È possibile visualizzare il codice sorgente di funzioni non esportate utilizzando l' :::operatore o 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>

Funzioni che chiamano codice compilato

Si noti che "compilato" non si riferisce al codice R compilato in byte come creato dal pacchetto del compilatore . La <bytecode: 0x294e410>riga nell'output sopra indica che la funzione è compilata in byte e che è ancora possibile visualizzare l'origine dalla riga di comando R.

Funzioni che chiamata .C, .Call, .Fortran, .External, .Internal, o .Primitivechiedono punti di ingresso nel codice compilato, in modo da avere a guardare sorgenti del codice compilato, se si vuole comprendere appieno la funzione. Questo mirror GitHub del codice sorgente R è un posto decente per iniziare. La funzione pryr::show_c_sourcepuò essere uno strumento utile in quanto ti porterà direttamente a una pagina GitHub per .Internale .Primitivechiama. I pacchetti possono utilizzare .C, .Call, .Fortran, e .External; ma no .Internaloppure .Primitive, poiché vengono utilizzati per chiamare funzioni integrate nell'interprete R.

Le chiamate ad alcune delle funzioni precedenti possono utilizzare un oggetto anziché una stringa di caratteri per fare riferimento alla funzione compilata. In tali casi, l'oggetto è di classe "NativeSymbolInfo", "RegisteredNativeSymbol"o "NativeSymbol"; e la stampa dell'oggetto fornisce informazioni utili. Ad esempio, optimchiamate .External2(C_optimhess, res$par, fn1, gr1, con)(nota che C_optimhessnon è "C_optimhess"). optimè nel pacchetto stats, quindi puoi digitare stats:::C_optimhessper vedere le informazioni sulla funzione compilata chiamata.

Codice compilato in un pacchetto

Se si desidera visualizzare il codice compilato in un pacchetto, sarà necessario scaricare / decomprimere l'origine del pacchetto. I binari installati non sono sufficienti. Il codice sorgente di un pacchetto è disponibile dallo stesso repository CRAN (o CRAN compatibile) da cui il pacchetto è stato originariamente installato. La download.packages()funzione può ottenere l'origine del pacchetto per te.

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

Ciò scaricherà la versione di origine del pacchetto Matrix e salverà il .tar.gzfile corrispondente nella directory corrente. Il codice sorgente per le funzioni compilate si trova nella srcdirectory del file non compresso e non tarato. La fase di decompressione e non annotazione può essere eseguita all'esterno Ro dall'interno Rutilizzando la untar()funzione. È possibile combinare il passaggio di download ed espansione in una singola chiamata (si noti che in questo modo è possibile scaricare e decomprimere un solo pacchetto alla volta):

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

In alternativa, se lo sviluppo del pacchetto è ospitato pubblicamente (ad es. Tramite GitHub , R-Forge o RForge.net ), probabilmente è possibile sfogliare il codice sorgente online.

Codice compilato in un pacchetto base

Alcuni pacchetti sono considerati pacchetti "base". Questi pacchetti vengono forniti con R e la loro versione è bloccato alla versione di R. Esempi includono base, compiler, stats, e utils. Pertanto, non sono disponibili come pacchetti scaricabili separati su CRAN come descritto sopra. Piuttosto, fanno parte dell'albero dei sorgenti R nelle singole directory dei pacchetti sotto /src/library/. Come accedere alla fonte R è descritto nella sezione successiva.

Codice compilato incorporato nell'interprete R.

Se si desidera visualizzare il codice integrato nell'interprete R, sarà necessario scaricare / decomprimere i sorgenti R; oppure puoi visualizzare le fonti online tramite il repository R Subversion o il mirror github di Winston Chang .

L' articolo di R (PDF) di Uwe Ligges (p. 43) è un buon riferimento generale su come visualizzare il codice sorgente .Internale le .Primitivefunzioni. I passaggi di base sono innanzitutto cercare il nome della funzione in src/main/names.ce quindi cercare il nome "C-entry" nei file in src/main/*.


71
Se lo usi RStudio, tenterà di estrarre la fonte per la funzione su cui si trova il cursore di testo se premi il F2tasto.
Ari B. Friedman,

1
@Ari B. Friedman Ci scusiamo per questa domanda tardiva. RStudio estrarrà anche il codice sorgente C per la funzione o solo per le funzioni scritte in R? Grazie
Sunny

3
@Samir Credo che sia solo la fonte R.
Ari B. Friedman,

@ AriB.Friedman - grazie Ari, questo è utile. Nel mio caso avevo ancora bisogno delle conoscenze mostrate nella risposta ( scale, che è S3 - ho ottenuto UseMethod("scale")e poi usato getAnywhere(scale.default)). Ma le semplici funzioni funzionano bene.
Tomasz Gandor,

2
L'imitazione è la più sincera forma di adulazione suppongo che questa risposta / wiki sia venuta prima :) Prima di questo rfaqs.com/source-code-of-r-method
JimLohse

94

Oltre alle altre risposte a questa domanda e ai suoi duplicati, ecco un buon modo per ottenere il codice sorgente per una funzione di pacchetto senza bisogno di sapere in quale pacchetto si trova. Ad esempio, se vogliamo la fonte per randomForest::rfcv():

Per visualizzarlo / modificarlo in una finestra pop-up:

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

Per reindirizzare a un file separato :

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

Certo, getAnywhere è un'altra stravagante scelta di nome R per qualcosa che avrebbe dovuto essere chiamato findOnSearchPath o simile.
smci,

1
Valuterò questa risposta perché mi ha avvicinato a ciò che volevo. Quello che volevo veramente, in RStudio, era View(foo); dove fooera una funzione da un pacchetto già caricato.
Sigfried,

1
@Sigfried: edit()apre un editor di testo (a scelta dell'utente) , mentre View()apre un visualizzatore di fogli di calcolo di tipo Excel per i dati , quest'ultimo è buono per la navigazione di dati (multi-colonna), ma di solito è terribile per il codice di qualsiasi cosa diversa dalla lunghezza del giocattolo. Ad esempio, come suggerisco, generalmente la prima cosa che voglio fare quando sfoglio una funzione è saltare / comprimere / manichino tutta la logica arg-parsing e default-action, per vedere cosa fa effettivamente la funzione .
smci

25

Viene rivelato quando si esegue il debug utilizzando la funzione debug (). Supponiamo di voler vedere il codice sottostante nella funzione di trasposizione t (). Basta digitare 't', non rivela molto.

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

Ma, usando il 'debug (functionName)', rivela il codice sottostante, senza gli interni.

> 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 () realizza lo stesso senza dover usare undebug ()


Gli svantaggi di questo metodo rispetto a quelli indicati nella risposta accettata sono la necessità di una chiamata di funzione funzionante (tutti i parametri necessari specificati, accettabilmente); e che, oltre al blocco iniziale di codice, ottieni anche ogni blocco nel momento in cui viene eseguito. Questo è ottimo per il debug, ma non ottimale solo per ottenere il sorgente.
Brian Diggs,

Sì, non è ottimale. Ma se sei intelligente, puoi ottenere la fonte veloce e sporca, specialmente per le funzioni integrate.
Selva,

2
Consiglierei anche di utilizzare debugonceinvece che debugin questo caso.
Joshua Ulrich,

20

Per le funzioni non primitive, R base include una funzione chiamata body()che restituisce il corpo della funzione. Ad esempio print.Date(), è possibile visualizzare l'origine della funzione:

body(print.Date)

produrrà questo:

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

Se stai lavorando in uno script e desideri che il codice funzione sia un vettore di caratteri, puoi ottenerlo.

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

ti porterà:

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

Perché dovrei voler fare una cosa del genere? Stavo creando un oggetto S3 personalizzato ( x, dove class(x) = "foo") basato su un elenco. Uno dei membri dell'elenco (chiamato "divertimento") era una funzione e volevo print.foo()visualizzare il codice sorgente della funzione, rientrato. Quindi ho finito con il seguente frammento di print.foo():

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

che rientra e visualizza il codice associato x[["fun"]].


18

Non ho visto come questo si adatta al flusso della risposta principale, ma mi ha sconcertato per un po ', quindi lo sto aggiungendo qui:

Operatori Infix

Per visualizzare il codice sorgente di alcuni operatori di base infisso (ad esempio, %%, %*%, %in%), uso getAnywhere, ad esempio:

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

La risposta principale spiega come utilizzare quindi i mirror per scavare più a fondo.


6
La risposta di SMCI raccomandato getAnywhere. Oppure si potrebbe utilizzare backticks se si conosce già il nome del gestore: `%in%`.
Joshua Ulrich,

3
@JoshuaUlrich non sapeva che potevi usare i backtick! Grazie. getAnywhereè menzionato anche nella tua risposta, ma penso che un riferimento specifico a infix sia utile per riferimento futuro a questa risposta - ho letto questa pagina molte volte ed ero ancora un po 'perplesso cercando di trovare il codice per tali funzioni per un mentre - e non pensavo che si adattasse al flusso di nessuna altra risposta (che entrambi utilizzano getAnywhereper un altro scopo).
MichaelChirico,

10

C'è una funzione molto utile in R edit

new_optim <- edit(optim)

Si aprirà il codice sorgente optimdell'uso dell'editor specificato in R options, quindi è possibile modificarlo e assegnare la funzione modificata a new_optim. Mi piace molto questa funzione per visualizzare il codice o per eseguire il debug del codice, ad esempio, stampare alcuni messaggi o variabili o persino assegnarli a una variabile globale per ulteriori indagini (ovviamente è possibile utilizzaredebug ).

Se vuoi solo visualizzare il codice sorgente e non vuoi che il fastidioso codice sorgente lungo venga stampato sulla tua console, puoi usarlo

invisible(edit(optim))

Chiaramente, questo non può essere usato per visualizzare il codice sorgente C / C ++ o Fortran.

A proposito, è editpossibile aprire altri oggetti come elenco, matrice, ecc., Che quindi mostra anche la struttura dei dati con attributi. La funzione depuò essere utilizzata per aprire un editor come Excel (se la GUI lo supporta) per modificare la matrice o il frame di dati e restituire quello nuovo. Questo è utile a volte, ma dovrebbe essere evitato nel solito caso, specialmente quando la matrice è grande.


3
Questo approccio fa apparire solo la stessa sorgente di funzione fornita dalla stampa (ovvero la stessa della domanda). Ottenere di più / più in profondità di questo è di cosa tratta questa domanda.
Brian Diggs,

2
@BrianDiggs Sì, hai ragione. Non intendevo dare una risposta alla domanda, dal momento che Joshua ha dato una risposta abbastanza completa. Cerco solo di aggiungere qualcosa di correlato all'argomento, interessante e può essere utile conoscerlo.
Eric,

8

Finché la funzione è scritta in puro R non C / C ++ / Fortran, si può usare quanto segue. Altrimenti il ​​modo migliore è il debug e l'utilizzo di " jump into ":

> functionBody(functionName)

2
Questo è lo stesso di body. identical(functionBody, body)lo è TRUE.
Joshua Ulrich,

1
base::bodye methods::functionBody, anche se sono sgradevoli per essere detah. bodypotrebbe anche essere sovrascritto: rdocumentation.org/search?q=body
Moody_Mudskipper

7

In RStudio, ci sono (almeno) 3 modi:

  1. Premere il tasto F2 mentre il cursore è su qualsiasi funzione.
  2. Fare clic sul nome della funzione tenendo premuto Ctrl o Comando
  3. View(nome_funzione) (come indicato sopra)

Si aprirà un nuovo riquadro con il codice sorgente. Se raggiungi .Primitive o .C avrai bisogno di un altro metodo, scusa.


5

View([function_name])- per esempio. View(mean)Assicurati di usare maiuscole [V]. Il codice di sola lettura si aprirà nell'editor.


5

Puoi anche provare a usare print.function(), che è S3 generico, per ottenere la funzione di scrittura nella console.


3
print.function()è un metodo S3 . Il generico è print(). E in genere non è una buona idea chiamare i metodi direttamente. Ciò vanifica l'intero scopo delle funzioni generiche e dell'invio del metodo.
Joshua Ulrich,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.