¿Cómo puedo ver el código fuente de una función?


551

Quiero ver el código fuente de una función para ver cómo funciona. Sé que puedo imprimir una función escribiendo su nombre en el indicador:

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

En este caso, ¿qué UseMethod("t")significa? ¿Cómo encuentro el código fuente que realmente está siendo utilizado, por ejemplo t(1:10):?

¿Hay alguna diferencia entre cuando veo UseMethody cuando veo standardGenericy showMethods, como 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.

En otros casos, puedo ver que se están llamando funciones R, pero no puedo encontrar el código fuente para esas funciones.

> 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

¿Cómo encuentro funciones como .cbindtsy .makeNamesTs?

En otros casos, hay un poco de código R, pero la mayor parte del trabajo parece hacerse en otro lugar.

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

¿Cómo averiguo qué hace la .Primitivefunción? Del mismo modo, algunas funciones llaman .C, .Call, .Fortran, .External, o .Internal. ¿Cómo puedo encontrar el código fuente para esos?




Respuestas:


518

UseMethod("t")te dice que t()es un ( S3 función genérica ) que tiene métodos para diferentes clases de objetos.

El sistema de despacho del método S3

Para las clases S3, puede usar la methodsfunción para enumerar los métodos para una función o clase genérica en particular.

> 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

"Las funciones no visibles están marcadas con un asterisco" significa que la función no se exporta desde el espacio de nombres de su paquete. Aún puede ver su código fuente a través de la :::función (es decir stats:::t.ts), o usando getAnywhere(). getAnywhere()es útil porque no tiene que saber de qué paquete proviene la función.

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

El sistema de despacho del método S4

El sistema S4 es un sistema de envío de método más nuevo y es una alternativa al sistema S3. Aquí hay un ejemplo de una función 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.

La salida ya ofrece mucha información. standardGenerices un indicador de una función S4. El método para ver los métodos S4 definidos se ofrece de manera útil:

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

getMethod se puede usar para ver el código fuente de uno de los métodos:

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

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

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

También hay métodos con firmas más complejas para cada método, por ejemplo

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"

Para ver el código fuente de uno de estos métodos, debe proporcionarse la firma completa, p. Ej.

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

No será suficiente proporcionar la firma parcial

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

Funciones que llaman funciones no exportadas

En el caso de ts.union, .cbindtsy .makeNamesTsson funciones no exportadas del statsespacio de nombres. Puede ver el código fuente de funciones no exportadas utilizando el :::operador 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>

Funciones que llaman código compilado

Tenga en cuenta que "compilado" no se refiere al código R compilado en bytes creado por el paquete del compilador . La <bytecode: 0x294e410>línea en la salida anterior indica que la función está compilada en bytes, y aún puede ver la fuente desde la línea de comando R.

Funciones esa llamada .C, .Call, .Fortran, .External, .Internal, o .Primitiveestán llamando a los puntos de entrada en el código compilado, por lo que tendrá que buscar en las fuentes del código compilado, si se quiere entender completamente la función. Este espejo GitHub del código fuente R es un lugar decente para comenzar. La función pryr::show_c_sourcepuede ser una herramienta útil, ya que le llevará directamente a una página de GitHub para .Internaly .Primitivellamadas. Los paquetes pueden utilizar .C, .Call, .Fortran, y .External; pero no .Internalo .Primitiveporque se usan para llamar a funciones integradas en el intérprete R.

Las llamadas a algunas de las funciones anteriores pueden usar un objeto en lugar de una cadena de caracteres para hacer referencia a la función compilada. En esos casos, el objeto es de la clase "NativeSymbolInfo", "RegisteredNativeSymbol"o "NativeSymbol"; e imprimir el objeto produce información útil. Por ejemplo, optimllamadas .External2(C_optimhess, res$par, fn1, gr1, con)(tenga en cuenta que C_optimhessno "C_optimhess"). optimestá en el paquete de estadísticas, por lo que puede escribir stats:::C_optimhesspara ver información sobre la función compilada que se llama.

Código compilado en un paquete

Si desea ver el código compilado en un paquete, deberá descargar / desempaquetar la fuente del paquete. Los binarios instalados no son suficientes. El código fuente de un paquete está disponible en el mismo repositorio CRAN (o compatible con CRAN) desde el que se instaló originalmente el paquete. La download.packages()función puede obtener la fuente del paquete por usted.

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

Esto descargará la versión fuente del paquete Matrix y guardará el .tar.gzarchivo correspondiente en el directorio actual. El código fuente para las funciones compiladas se puede encontrar en el srcdirectorio del archivo sin comprimir y sin tartar. El paso de descompresión y descompresión se puede realizar desde afuera Ro desde adentro Rusando la untar()función. Es posible combinar el paso de descarga y expansión en una sola llamada (tenga en cuenta que solo un paquete a la vez se puede descargar y desempaquetar de esta manera):

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

Alternativamente, si el desarrollo del paquete está alojado públicamente (por ejemplo, a través de GitHub , R-Forge o RForge.net ), probablemente pueda buscar el código fuente en línea.

Código compilado en un paquete base

Ciertos paquetes se consideran paquetes "básicos". Estos paquetes se envían con R y su versión se bloquea en la versión de R. Los ejemplos incluyen base, compiler, stats, y utils. Como tales, no están disponibles como paquetes descargables por separado en CRAN como se describió anteriormente. Más bien, son parte del árbol de origen R en directorios de paquetes individuales en /src/library/. Cómo acceder a la fuente R se describe en la siguiente sección.

Código compilado integrado en el intérprete R

Si desea ver el código incorporado en el intérprete de R, deberá descargar / desempaquetar las fuentes de R; o puede ver las fuentes en línea a través del repositorio R Subversion o el espejo github de Winston Chang .

El artículo de noticias R de Uwe Ligges (PDF) (p. 43) es una buena referencia general de cómo ver el código fuente .Internaly las .Primitivefunciones. Los pasos básicos son buscar primero el nombre de la función src/main/names.cy luego buscar el nombre de "entrada C" en los archivos de src/main/*.


71
Si lo usa RStudio, intentará extraer la fuente de la función sobre la que termina el cursor de texto si presiona la F2tecla.
Ari B. Friedman

1
@Ari B. Friedman Perdón por esta pregunta tardía. ¿RStudio también extraerá el código fuente C para la función o solo para las funciones escritas en R? Gracias
Sunny

3
@ Samir Creo que es solo la fuente R.
Ari B. Friedman

@ AriB.Friedman: gracias Ari, esto es útil. En mi caso, todavía necesitaba el conocimiento que se muestra en la respuesta ( scaleque es S3: obtuve UseMethod("scale")y luego lo usé getAnywhere(scale.default)). Pero las funciones simples funcionan bien.
Tomasz Gandor

2
La imitación es la forma más sincera de adulación. Supongo que esta respuesta / wiki fue lo primero :) Antes de esto rfaqs.com/source-code-of-r-method
JimLohse

94

Además de las otras respuestas a esta pregunta y sus duplicados, esta es una buena manera de obtener el código fuente para una función de paquete sin necesidad de saber en qué paquete está. Por ejemplo, si queremos la fuente para randomForest::rfcv():

Para verlo / editarlo en una ventana emergente:

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

Para redirigir a un archivo separado :

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

Es cierto que getAnywhere es otra elección de nombre R extraña para algo que debería haberse llamado findOnSearchPath o similar.
smci

1
Votaré esta respuesta porque me acercó a lo que quería. Lo que realmente quería, en RStudio, era View(foo); donde foofue una función de un paquete ya cargado.
Sigfried

1
@Sigfried: edit()abre un editor de texto (a elección del usuario) , mientras que View()abre un visor de hoja de cálculo de tipo Excel para datos , este último es bueno para examinar datos (multicolumnares), pero generalmente es terrible para el código de cualquier otra cosa que no sea la longitud del juguete. Por ejemplo, como insinúo, generalmente lo primero que quiero hacer al examinar una función es omitir / colapsar / descartar toda la lógica de análisis de argumentos y acción predeterminada, para ver qué hace realmente la función .
smci

25

Se revela cuando depura usando la función debug (). Suponga que desea ver el código subyacente en la función de transposición t (). Simplemente escribiendo 't', no revela mucho.

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

Pero, al usar el 'debug (functionName)', revela el código subyacente, sin los elementos internos.

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

EDITAR: debugonce () logra lo mismo sin tener que usar undebug ()


Las desventajas de este método en comparación con las dadas en la respuesta aceptada son que necesita una llamada a la función de trabajo (todos los parámetros necesarios especificados, aceptablemente); y que, además del bloque inicial de código, también obtiene cada bloque en el momento en que se ejecuta. Esto es excelente para la depuración, pero no es óptimo solo para obtener la fuente.
Brian Diggs el

Sí, no es óptimo. Pero si es inteligente, puede obtener la fuente rápida y sucia, especialmente para las funciones integradas.
Selva

2
También recomendaría usar en debugoncelugar de debugen este caso.
Joshua Ulrich

20

Para funciones no primitivas, R base incluye una función llamada body()que devuelve el cuerpo de la función. Por ejemplo print.Date(), se puede ver la fuente de la función:

body(print.Date)

producirá esto:

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

Si está trabajando en un script y quiere el código de función como un vector de caracteres, puede obtenerlo.

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

te conseguirá:

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

¿Por qué querría hacer algo así? Estaba creando un objeto S3 personalizado ( x, donde class(x) = "foo") basado en una lista. Uno de los miembros de la lista (llamado "diversión") era una función y quería print.foo()mostrar el código fuente de la función, sangrado. Así que terminé con el siguiente fragmento en print.foo():

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

que sangra y muestra el código asociado con x[["fun"]].


18

No vi cómo esto encajaba en el flujo de la respuesta principal, pero me dejó perplejo por un tiempo, así que lo agrego aquí:

Operadores de infijo

Para ver el código fuente de algunos operadores infijos base (por ejemplo, %%, %*%, %in%), el uso getAnywhere, por ejemplo:

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 respuesta principal cubre cómo usar los espejos para cavar más profundo.


66
La respuesta de smci recomendada getAnywhere. O usted podría utilizar acentos abiertos si ya conoce el nombre del operador: `%in%`.
Joshua Ulrich

3
¡@JoshuaUlrich no sabía que podía usar backticks! Gracias. getAnywheretambién se menciona en su respuesta, pero creo que una referencia específica a infix es útil para referencia futura a esta respuesta: he leído esta página muchas veces y todavía estaba un poco perplejo tratando de encontrar código para tales funciones para un while, y no pensé que encajara en el flujo de ninguna otra respuesta (que ambas están usando getAnywherepara otro propósito).
MichaelChirico

10

Hay una función muy útil en R edit

new_optim <- edit(optim)

Se abrirá el código fuente del optimuso del editor especificado en R options, y luego podrá editarlo y asignarle la función modificada new_optim. Me gusta mucho esta función para ver el código o para depurar el código, por ejemplo, imprimir algunos mensajes o variables o incluso asignarlos a variables globales para una mayor investigación (por supuesto, puede usar debug).

Si solo desea ver el código fuente y no desea que se imprima el molesto código fuente largo en su consola, puede usar

invisible(edit(optim))

Claramente, esto no se puede usar para ver el código fuente de C / C ++ o Fortran.

Por cierto, editpuede abrir otros objetos como lista, matriz, etc., que luego muestra la estructura de datos con atributos también. La función dese puede utilizar para abrir un editor similar a Excel (si la GUI lo admite) para modificar la matriz o el marco de datos y devolver el nuevo. Esto es útil a veces, pero debe evitarse en el caso habitual, especialmente cuando la matriz es grande.


3
Este enfoque solo muestra la misma fuente de función que imprime la función (es decir, lo mismo que en la pregunta). Ir más allá / más profundo que eso es de lo que se trata esta pregunta.
Brian Diggs

2
@BrianDiggs Sí, tienes razón. No quise dar una respuesta a la pregunta, ya que Joshua ha dado una respuesta bastante completa. Solo trato de agregar algo relacionado con el tema, interesante y puede ser útil saberlo.
Eric

8

Mientras la función esté escrita en R puro, no en C / C ++ / Fortran, se puede usar lo siguiente. De lo contrario, la mejor manera es depurar y usar " saltar a ":

> functionBody(functionName)

2
Esto es lo mismo que body. identical(functionBody, body)es TRUE.
Joshua Ulrich el

1
base::bodyy methods::functionBody, aunque no es probable que los separen. bodytambién podría ser anulado: rdocumentation.org/search?q=body
Moody_Mudskipper

7

En RStudio, hay (al menos) 3 formas:

  1. Presione la tecla F2 mientras el cursor está en cualquier función.
  2. Haga clic en el nombre de la función mientras mantiene presionada la tecla Ctrl o Comando
  3. View(nombre_función) (como se indicó anteriormente)

Se abrirá un nuevo panel con el código fuente. Si llega a .Primitive o .C necesitará otro método, lo siento.


5

View([function_name])- p.ej. View(mean)Asegúrese de usar mayúsculas [V]. El código de solo lectura se abrirá en el editor.


5

También puede intentar usar print.function(), que es S3 genérico, para que la función escriba en la consola.


3
print.function()Es un método S3 . El genérico es print(). Y generalmente no es una buena idea llamar a los métodos directamente. Eso anula todo el propósito de las funciones genéricas y el envío de métodos.
Joshua Ulrich
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.