Como posso visualizar o código fonte de uma função?


551

Eu quero olhar para o código fonte de uma função para ver como ele funciona. Sei que posso imprimir uma função digitando seu nome no prompt:

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

Nesse caso, o que UseMethod("t")significa? Como localizo o código fonte que realmente está sendo usado, por exemplo t(1:10):?

Existe uma diferença entre quando eu vejo UseMethode quando eu vejo standardGenerice showMethods, como acontece com 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.

Em outros casos, posso ver que as funções R estão sendo chamadas, mas não consigo encontrar o código-fonte para essas funções.

> 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

Como encontro funções como .cbindtse .makeNamesTs?

Em outros casos, ainda há um pouco de código R, mas a maior parte do trabalho parece ser feita em outro 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")

Como descubro o que a .Primitivefunção faz? Da mesma forma, algumas funções chamar .C, .Call, .Fortran, .External, ou .Internal. Como posso encontrar o código fonte para esses?




Respostas:


518

UseMethod("t")está dizendo que t()é uma função genérica ( S3 ) que possui métodos para diferentes classes de objetos.

O sistema de despacho do método S3

Para classes S3, você pode usar a methodsfunção para listar os métodos para uma função ou classe genérica específica.

> 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

"Funções não visíveis estão com asterisco" significa que a função não é exportada do espaço para nome do pacote. Você ainda pode visualizar seu código fonte através da :::função (ou seja stats:::t.ts) ou usando getAnywhere(). getAnywhere()é útil porque você não precisa saber de qual pacote a função veio.

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

O sistema de despacho do método S4

O sistema S4 é um sistema de despacho de método mais recente e é uma alternativa ao sistema S3. Aqui está um exemplo de uma função 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.

A saída já oferece muita informação. standardGenericé um indicador de uma função S4. O método para ver os métodos S4 definidos é oferecido de maneira útil:

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

getMethod pode ser usado para ver o código fonte de um dos 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"

Também existem métodos com assinaturas mais complexas para cada método, por exemplo

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 o código fonte de um desses métodos, toda a assinatura deve ser fornecida, por exemplo

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

Não será suficiente fornecer a assinatura parcial

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

Funções que chamam funções não exportadas

No caso de ts.union, .cbindtse .makeNamesTssão funções não exportadas do statsespaço para nome. Você pode visualizar o código fonte de funções não exportadas usando o :::operador ou 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>

Funções que chamam código compilado

Observe que "compilado" não se refere ao código R compilado em bytes, criado pelo pacote do compilador . A <bytecode: 0x294e410>linha na saída acima indica que a função é compilada em bytes e você ainda pode visualizar a fonte na linha de comando R.

Funções que chamada .C, .Call, .Fortran, .External, .Internal, ou .Primitiveestão chamando pontos de entrada no código compilado, assim que você tem que olhar para as fontes do código compilado, se você quiser entender completamente a função. Esse espelho do código-fonte do GitHub é um lugar decente para começar. A função pryr::show_c_sourcepode ser uma ferramenta útil, pois o levará diretamente a uma página do GitHub para chamadas .Internale .Primitivechamadas. Os pacotes podem usar .C, .Call, .Fortran, e .External; mas não .Internalou .Primitive, porque são usados ​​para chamar funções incorporadas ao intérprete R.

Chamadas para algumas das funções acima podem usar um objeto em vez de uma cadeia de caracteres para fazer referência à função compilada. Nesses casos, o objectivo é de classe "NativeSymbolInfo", "RegisteredNativeSymbol", ou "NativeSymbol"; e imprimir o objeto produz informações úteis. Por exemplo, optimchamadas .External2(C_optimhess, res$par, fn1, gr1, con)(observe que C_optimhessnão "C_optimhess"). optimestá no pacote de estatísticas, então você pode digitar stats:::C_optimhesspara ver informações sobre a função compilada que está sendo chamada.

Código compilado em um pacote

Se você deseja visualizar o código compilado em um pacote, será necessário baixar / descompactar a fonte do pacote. Os binários instalados não são suficientes. O código-fonte de um pacote está disponível no mesmo repositório CRAN (ou compatível com CRAN) do qual o pacote foi originalmente instalado. A download.packages()função pode obter a fonte do pacote para você.

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

Isso fará o download da versão de origem do pacote Matrix e salvará o .tar.gzarquivo correspondente no diretório atual. O código-fonte para funções compiladas pode ser encontrado no srcdiretório do arquivo descompactado e não-marcado. A etapa de descompactação e descompactação pode ser feita fora Rou a partir do interior Rusando a untar()função É possível combinar a etapa de download e expansão em uma única chamada (observe que apenas um pacote de cada vez pode ser baixado e descompactado desta maneira):

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

Como alternativa, se o desenvolvimento do pacote for hospedado publicamente (por exemplo, via GitHub , R-Forge ou RForge.net ), você provavelmente poderá procurar o código-fonte on-line.

Código compilado em um pacote base

Certos pacotes são considerados pacotes "base". Estes pacotes fornecidos com R e sua versão é bloqueado para a versão de R. Exemplos incluem base, compiler, stats, e utils. Como tal, eles não estão disponíveis como pacotes para download separados no CRAN, conforme descrito acima. Em vez disso, eles fazem parte da árvore de origem R em diretórios de pacotes individuais em /src/library/. Como acessar a fonte R é descrito na próxima seção.

Código compilado incorporado ao intérprete R

Se você deseja visualizar o código incorporado ao intérprete R, será necessário baixar / descompactar as fontes R; ou você pode visualizar as fontes on-line através do repositório R Subversion ou do espelho do github de Winston Chang .

O artigo de notícias R de Uwe Ligges (PDF) (p. 43) é uma boa referência geral de como visualizar o código fonte .Internale as .Primitivefunções. As etapas básicas são primeiro procurar o nome da função src/main/names.ce, em seguida, procurar o nome "C-entry" nos arquivos src/main/*.


71
Se você usar RStudio, ele tentará puxar a fonte para a função em que o cursor de texto acabou, se você pressionar a F2tecla.
Ari B. Friedman

1
@Ari B. Friedman Desculpe por esta pergunta tardia. O RStudio também extrairá o código fonte C para a função ou apenas para as funções escritas em R? Obrigado
Sunny

3
@ Samir Eu acredito que é apenas a fonte R.
Ari B. Friedman

@ AriB.Friedman - obrigado Ari, isso é útil. No meu caso, eu ainda precisava do conhecimento mostrado na resposta ( scaleque é S3 - eu obtive UseMethod("scale")e depois usei getAnywhere(scale.default)). Mas funções simples funcionam muito bem.
Tomasz Gandor

2
A imitação é a forma mais sincera de lisonja eu assumo esta resposta / wiki veio primeiro :) Antes desta rfaqs.com/source-code-of-r-method
JimLohse

94

Além das outras respostas sobre esta questão e suas duplicatas, aqui está uma boa maneira de obter o código-fonte para uma função do pacote sem precisar saber em qual pacote ele está. Por exemplo, se queremos a fonte para randomForest::rfcv():

Para visualizar / editar em uma janela pop-up:

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

Para redirecionar para um arquivo separado :

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

É certo que o getAnywhere é outra opção maluca de nome R para algo que deveria ter sido chamado de findOnSearchPath ou similar.
smci

1
Eu irei aprovar esta resposta porque me aproximou do que eu queria. O que eu realmente queria, no RStudio, era View(foo); onde fooera uma função de um pacote já carregado.
Sigfried

1
@ Sigfried: edit()abre um editor de texto (à escolha do usuário) , enquanto View()abre um visualizador de planilhas do tipo Excel para dados , o último é bom para navegar em dados (multi-colunares), mas geralmente péssimo para código que não seja o comprimento do brinquedo. Por exemplo, como eu sugiro, geralmente a primeira coisa que quero fazer ao procurar uma função é pular / recolher / manipular toda a lógica de análise de argumentos e ação padrão, para ver o que a função realmente faz .
smci 4/03/19

25

É revelado quando você depura usando a função debug (). Suponha que você queira ver o código subjacente na função de transposição t (). Apenas digitar 't', não revela muito.

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

Mas, usando o 'debug (functionName)', ele revela o código subjacente e sem os 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)

EDIT: debugonce () realiza o mesmo sem ter que usar undebug ()


As desvantagens deste método, em comparação com as fornecidas na resposta aceita, são que você precisa de uma chamada de função de trabalho (todos os parâmetros necessários especificados, aceitável); e que, além do bloco inicial de código, você também obtém cada bloco no momento em que é executado. Isso é ótimo para depuração, mas não é ideal apenas para obter a fonte.
Brian Diggs

Sim, não é o ideal. Mas se você for esperto, poderá obter a fonte rápida e suja, especialmente para funções embutidas.
Selva

2
Eu também recomendo usar em debugoncevez de debugneste caso.
18714 Joshua Ulrich

20

Para funções não primitivas, a base R inclui uma função chamada body()que retorna o corpo da função. Por exemplo, a fonte da print.Date()função pode ser visualizada:

body(print.Date)

produzirá o seguinte:

{
    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 você estiver trabalhando em um script e quiser o código de função como um vetor de caractere, poderá obtê-lo.

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

você receberá:

[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 que eu iria querer fazer uma coisa dessas? Eu estava criando um objeto S3 personalizado ( x, onde class(x) = "foo") com base em uma lista. Um dos membros da lista (chamado "divertido") era uma função e eu queria print.foo()exibir o código fonte da função, recuado. Então, acabei com o seguinte trecho em print.foo():

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

que recua e exibe o código associado x[["fun"]].


18

Não vi como isso se encaixava no fluxo da resposta principal, mas me surpreendeu por um tempo, então estou adicionando aqui:

Operadores Infix

Para ver o código fonte de alguns operadores de base infixas (por exemplo, %%, %*%, %in%), o uso getAnywhere, por exemplo:

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

A resposta principal aborda como usar espelhos para ir mais fundo.


6
Resposta da SMCI recomendada getAnywhere. Ou você pode simplesmente usar backticks se você já sabe o nome do operador: `%in%`.
Joshua Ulrich

3
@JoshuaUlrich não sabia que você poderia usar backticks! Obrigado. getAnywheretambém é mencionado na sua resposta, mas acho que uma referência específica ao infix é útil para referência futura a essa resposta - eu li essa página várias vezes e ainda estava um pouco perplexo tentando encontrar código para tais funções para um enquanto - e eu não achei que se encaixasse no fluxo de qualquer outra resposta (que ambos estão usando getAnywherepara outro propósito).
MichaelChirico

10

Existe uma função muito útil em R edit

new_optim <- edit(optim)

Ele abrirá o código-fonte do optimuso do editor especificado em R's optionse, em seguida, você poderá editá-lo e atribuir a função modificada new_optim. Eu gosto muito dessa função para visualizar ou depurar o código, por exemplo, imprimir algumas mensagens ou variáveis ​​ou até atribuí-las a variáveis ​​globais para uma investigação mais aprofundada (é claro que você pode usar debug).

Se você deseja apenas visualizar o código-fonte e não deseja que o código-fonte longo e irritante seja impresso no seu console, você pode usar

invisible(edit(optim))

Claramente, isso não pode ser usado para exibir o código-fonte C / C ++ ou Fortran.

BTW, editpode abrir outros objetos como lista, matriz, etc., que também mostram a estrutura de dados com atributos. A função depode ser usada para abrir um editor como o Excel (se a GUI suportar) para modificar a matriz ou o quadro de dados e retornar o novo. Às vezes, isso é útil, mas deve ser evitado no caso usual, principalmente quando a matriz é grande.


3
Essa abordagem traz apenas a mesma fonte de função fornecida pela impressão da função (ou seja, a mesma da pergunta). Ficar cada vez mais profundo do que isso é sobre esta questão.
Brian Diggs

2
@BrianDiggs Sim, você está certo. Não pretendi dar uma resposta à pergunta, já que Josué deu uma resposta bastante completa. Eu apenas tento adicionar algo relacionado ao tópico, interessante e que possa ser útil para você conhecer.
Eric

8

Desde que a função seja escrita em R puro e não em C / C ++ / Fortran, pode-se usar o seguinte. Caso contrário, a melhor maneira é depurar e usar " pular para ":

> functionBody(functionName)

2
É o mesmo que body. identical(functionBody, body)é TRUE.
21607 Joshua Ulrich

1
base::bodye methods::functionBody, embora seja improvável que sejam detidos. bodypoderia ser substituído também: rdocumentation.org/search?q=body
Moody_Mudskipper

7

No RStudio, existem (pelo menos) três maneiras:

  1. Pressione a tecla F2 enquanto o cursor estiver em qualquer função.
  2. Clique no nome da função enquanto pressiona Ctrl ou Command
  3. View(nome_da_função) (como indicado acima)

Um novo painel será aberto com o código fonte. Se você acessar .Primitive ou .C, precisará de outro método, desculpe.


5

View([function_name])- por exemplo. View(mean)Certifique-se de usar maiúsculas [V]. O código somente leitura será aberto no editor.


5

Você também pode tentar usar print.function(), que é o S3 genérico, para obter a função de gravação no console.


3
print.function()é um método S3 . O genérico é print(). E geralmente não é uma boa ideia chamar métodos diretamente. Isso anula todo o objetivo de funções genéricas e envio de métodos.
Joshua Ulrich
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.