Comment puis-je afficher le code source d'une fonction?

551

Je veux regarder le code source d'une fonction pour voir comment cela fonctionne. Je sais que je peux imprimer une fonction en tapant son nom à l'invite:

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

Dans ce cas, qu'est-ce que cela UseMethod("t")signifie? Comment puis-je trouver le code source actuellement utilisé par, par exemple t(1:10):?

Y a-t-il une différence entre quand je vois UseMethodet quand je vois standardGenericet showMethods, comme avec 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.

Dans d'autres cas, je peux voir que les fonctions R sont appelées, mais je ne trouve pas le code source de ces fonctions.

> 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

Comment trouver des fonctions comme .cbindtset .makeNamesTs?

Dans d'autres cas encore, il y a un peu de code R, mais la plupart du travail semble être fait ailleurs.

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

Comment savoir ce que fait la .Primitivefonction? De même, certaines fonctions appellent .C, .Call, .Fortran, .Externalou .Internal. Comment puis-je trouver le code source de ceux-ci?

Joshua Ulrich
la source
2
Voir aussi stackoverflow.com/q/1439348/134830
Richie Cotton

Réponses:

518

UseMethod("t")vous indique qu'il t()s'agit d'une fonction générique ( S3 ) qui a des méthodes pour différentes classes d'objets.

Le système de répartition de la méthode S3

Pour les classes S3, vous pouvez utiliser la methodsfonction pour répertorier les méthodes d'une fonction ou classe générique particulière.

> 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

"Les fonctions non visibles sont marquées d'un astérisque" signifie que la fonction n'est pas exportée depuis l'espace de noms de son package. Vous pouvez toujours voir son code source via la :::fonction (ie stats:::t.ts), ou en utilisant getAnywhere(). getAnywhere()est utile car vous n'avez pas besoin de savoir de quel paquet provient la fonction.

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

Le système de répartition de la méthode S4

Le système S4 ​​est un nouveau système de répartition des méthodes et une alternative au système S3. Voici un exemple de fonction 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 sortie offre déjà beaucoup d'informations. standardGenericest un indicateur d'une fonction S4. La méthode pour voir les méthodes S4 définies est offerte utilement:

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

getMethod peut être utilisé pour voir le code source de l'une des méthodes:

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

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

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

Il existe également des méthodes avec des signatures plus complexes pour chaque méthode, par exemple

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"

Pour voir le code source de l'une de ces méthodes, la signature entière doit être fournie, par exemple

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

Il ne suffira pas de fournir la signature partielle

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

Fonctions qui appellent des fonctions non exportées

Dans le cas de ts.union, .cbindtset .makeNamesTssont des fonctions non exportées de l' statsespace de noms. Vous pouvez afficher le code source des fonctions non exportées à l'aide de l' :::opérateur 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>

Fonctions qui appellent du code compilé

Notez que "compilé" ne fait pas référence au code R compilé en octets tel que créé par le package du compilateur . La <bytecode: 0x294e410>ligne dans la sortie ci-dessus indique que la fonction est compilée en octets, et vous pouvez toujours afficher la source à partir de la ligne de commande R.

Fonctions appel .C, .Call, .Fortran, .External, .Internalou .Primitiveappellent des points d'entrée dans le code compilé, de sorte que vous devrez regarder les sources du code compilé si vous voulez comprendre la fonction. Ce miroir GitHub du code source R est un bon endroit pour commencer. La fonction pryr::show_c_sourcepeut être un outil utile car elle vous mènera directement à une page GitHub pour .Internalet .Primitiveappels. Les packages peuvent utiliser .C,.Call , .Fortranet .External; mais pas .Internalou .Primitive, car ceux-ci sont utilisés pour appeler des fonctions intégrées à l'interpréteur R.

Les appels à certaines des fonctions ci-dessus peuvent utiliser un objet au lieu d'une chaîne de caractères pour référencer la fonction compilée. Dans ces cas, l'objet est de classe "NativeSymbolInfo", "RegisteredNativeSymbol"ou "NativeSymbol"; et l'impression de l'objet fournit des informations utiles. Par exemple, les optimappels .External2(C_optimhess, res$par, fn1, gr1, con)(notez queC_optimhess pas le cas "C_optimhess"). optimest dans le package de statistiques, vous pouvez donc taper stats:::C_optimhesspour voir les informations sur la fonction compilée appelée.

Code compilé dans un package

Si vous souhaitez afficher le code compilé dans un package, vous devrez télécharger / décompresser la source du package. Les binaires installés ne sont pas suffisants. Le code source d'un package est disponible à partir du même référentiel CRAN (ou compatible CRAN) à partir duquel le package a été initialement installé. ledownload.packages() fonction peut obtenir la source du package pour vous.

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

Cela va télécharger la version source du package Matrix et enregistrer le .tar.gzfichier correspondant dans le répertoire actuel. Le code source des fonctions compilées se trouve dans le srcrépertoire du fichier non compressé et non taré. L'étape de décompression et de décompression peut être effectuée à l'extérieur Rou à l'intérieur de Rla untar()fonction. Il est possible de combiner l'étape de téléchargement et d'extension en un seul appel (notez qu'un seul package à la fois peut être téléchargé et décompressé de cette manière):

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

Alternativement, si le développement du package est hébergé publiquement (par exemple via GitHub , R-Forge ou RForge.net ), vous pouvez probablement parcourir le code source en ligne.

Code compilé dans un package de base

Certains packages sont considérés comme des packages «de base». Ces paquets sont livrés avec R et leur version est verrouillé à la version des exemples R. comprennent base, compiler, statset utils. En tant que tels, ils ne sont pas disponibles en tant que packages téléchargeables séparés sur CRAN comme décrit ci-dessus. Ils font plutôt partie de l'arborescence source R dans les répertoires de packages individuels sous /src/library/. Comment accéder à la source R est décrit dans la section suivante.

Code compilé intégré à l'interpréteur R

Si vous souhaitez afficher le code intégré à l'interpréteur R, vous devrez télécharger / décompresser les sources R; ou vous pouvez afficher les sources en ligne via le référentiel R Subversion ou le miroir github de Winston Chang .

L' article de presse R d' Uwe Ligges (PDF) (p. 43) est une bonne référence générale sur la façon d'afficher le code source .Internalet les .Primitivefonctions. Les étapes de base consistent à rechercher d'abord le nom de la fonction dans src/main/names.c, puis à rechercher le nom "C-entry" dans les fichiers de src/main/*.

Joshua Ulrich
la source
71
Si vous utilisez RStudio, il tentera d'extraire la source de la fonction sur laquelle se trouve votre curseur de texte si vous appuyez sur la F2touche.
Ari B. Friedman
1
@Ari B. Friedman Désolé pour cette question tardive. RStudio tirera-t-il également le code source C pour la fonction ou simplement pour les fonctions écrites en R? Merci
Sunny
3
@Samir Je crois que c'est juste la source R.
Ari B. Friedman
@ AriB.Friedman - merci Ari, c'est pratique. Dans mon cas, j'avais toujours besoin des connaissances indiquées dans la réponse ( scale, qui est S3 - que j'ai acquise UseMethod("scale")puis utilisée getAnywhere(scale.default)). Mais les fonctions simples fonctionnent très bien.
Tomasz Gandor
2
L'imitation est la forme de flatterie la plus sincère Je suppose que cette réponse / wiki est venu en premier :) Avant cette rfaqs.com/source-code-of-r-method
JimLohse
94

En plus des autres réponses à cette question et ses doublons, voici un bon moyen d'obtenir le code source d'une fonction de package sans avoir besoin de savoir dans quel package elle se trouve. Par exemple, si nous voulons la source pour randomForest::rfcv():

Pour l' afficher / le modifier dans une fenêtre contextuelle:

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

Pour rediriger vers un fichier distinct :

capture.output(getAnywhere('rfcv'), file='source_rfcv.r')
smci
la source
Certes, getAnywhere est un autre choix de nom R farfelu pour quelque chose qui aurait dû être appelé findOnSearchPath ou similaire.
smci
1
Je vais voter pour cette réponse parce qu'elle m'a rapproché de ce que je voulais. Ce que je voulais réellement, dans RStudio, c'était View(foo); où fooétait une fonction d'un package déjà chargé.
Sigfried
1
@Sigfried: edit()ouvre un éditeur de texte (au choix de l'utilisateur) , alors qu'il View()ouvre un visualiseur de type Excel pour les données , ce dernier est bon pour parcourir les données (multi-colonnes), mais généralement terrible pour le code autre que la longueur du jouet. Par exemple , comme j'allusion à, généralement la première chose que je veux faire lorsque vous naviguez sur une fonction de saut / effondrement / factice toutes les arg-analyse syntaxique et logique action par défaut, pour voir ce que la fonction fait fait .
smci
25

Il est révélé lorsque vous déboguez à l'aide de la fonction debug (). Supposons que vous souhaitiez voir le code sous-jacent dans la fonction de transposition t (). Le simple fait de taper 't' ne révèle pas grand-chose.

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

Mais, en utilisant le 'debug (functionName)', il révèle le code sous-jacent, sans les internes.

> 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 () accomplit la même chose sans avoir à utiliser undebug ()

Selva
la source
Les inconvénients de cette méthode par rapport à ceux donnés dans la réponse acceptée sont que vous avez besoin d'un appel de fonction de travail (tous les paramètres nécessaires spécifiés, de manière acceptable); et qu'en plus du bloc de code initial, vous obtenez également chaque bloc au moment de son exécution. C'est idéal pour le débogage, mais pas optimal pour obtenir simplement la source.
Brian Diggs
Oui, ce n'est pas optimal. Mais si vous êtes intelligent, vous pouvez obtenir la source rapide et sale, en particulier pour les fonctions intégrées.
Selva
2
Je recommanderais également d'utiliser debugonceau lieu de debugdans ce cas.
Joshua Ulrich
20

Pour les fonctions non primitives, R base inclut une fonction appelée body()qui renvoie le corps de la fonction. Par exemple, la source de la print.Date()fonction peut être consultée:

body(print.Date)

produira ceci:

{
    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 vous travaillez dans un script et que vous souhaitez que le code de fonction soit un vecteur de caractères, vous pouvez l'obtenir.

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

vous obtiendrez:

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

Pourquoi voudrais-je faire une telle chose? Je créais un objet S3 personnalisé ( x, où class(x) = "foo") basé sur une liste. Un des membres de la liste (nommé "fun") était une fonction et je voulais print.foo()afficher le code source de la fonction, en retrait. Je me suis donc retrouvé avec l'extrait de code suivant dans print.foo():

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

qui indente et affiche le code associé à x[["fun"]].

Geoffrey Poole
la source
18

Je n'ai pas vu comment cela s'inscrivait dans le flux de la réponse principale, mais cela m'a embarrassé pendant un moment, donc je l'ajoute ici:

Opérateurs Infix

Pour voir le code source de certains opérateurs infixes de base (par exemple %%, %*%, %in%), l' utilisation getAnywhere, par exemple:

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 réponse principale couvre comment utiliser ensuite les miroirs pour creuser plus profondément.

MichaelChirico
la source
6
La réponse de smci est recommandée getAnywhere. Ou vous pouvez simplement utiliser si vous connaissez des accents graves déjà le nom de l'opérateur: `%in%`.
Joshua Ulrich
3
@JoshuaUlrich ne savait pas que vous pouviez utiliser des backticks! Merci. getAnywhereest également mentionné dans votre réponse, mais je pense qu'une référence spécifique à infixe est utile pour une référence future à cette réponse - j'ai lu cette page plusieurs fois et j'étais encore un peu perplexe en essayant de trouver du code pour de telles fonctions pour un tandis que - et je ne pensais pas que cela s'inscrivait dans le flux de l'une ou l'autre autre réponse (qui sont toutes deux utilisées getAnywheredans un autre but).
MichaelChirico
10

Il y a une fonction très pratique dans R edit

new_optim <- edit(optim)

Il ouvrira le code source de l' optimutilisation de l'éditeur spécifié dans les R options, puis vous pourrez le modifier et affecter la fonction modifiée à new_optim. J'aime beaucoup cette fonction pour afficher le code ou déboguer le code, par exemple, imprimer certains messages ou variables ou même les affecter à des variables globales pour une enquête plus approfondie (bien sûr, vous pouvez utiliser debug).

Si vous souhaitez simplement afficher le code source et ne souhaitez pas que le long code source ennuyeux soit imprimé sur votre console, vous pouvez utiliser

invisible(edit(optim))

De toute évidence, cela ne peut pas être utilisé pour afficher le code source C / C ++ ou Fortran.

BTW, editpeut ouvrir d'autres objets comme la liste, la matrice, etc., qui montre également la structure des données avec des attributs. La fonction depeut être utilisée pour ouvrir un éditeur de type Excel (si l'interface graphique le prend en charge) pour modifier la matrice ou le bloc de données et renvoyer le nouveau. Ceci est parfois pratique, mais devrait être évité dans les cas habituels, en particulier lorsque votre matrice est grande.

Eric
la source
3
Cette approche ne fait apparaître que la même source de fonction que l'impression de la fonction (c'est-à-dire la même que dans la question). Aller plus loin / plus profondément que cela est le sujet de cette question.
Brian Diggs
2
@BrianDiggs Oui, vous avez raison. Je ne voulais pas répondre à la question, car Josué a donné une réponse assez complète. J'essaie simplement d'ajouter quelque chose en rapport avec le sujet, intéressant et peut être utile à connaître.
Eric
8

Tant que la fonction est écrite en R pur et non en C / C ++ / Fortran, on peut utiliser ce qui suit. Sinon, la meilleure façon est de déboguer et d'utiliser " jump into ":

> functionBody(functionName)
MCH
la source
2
C'est la même chose que body. identical(functionBody, body)est TRUE.
Joshua Ulrich
1
base::bodyet methods::functionBody, bien qu'ils ne soient pas susceptibles d'être détruits. bodypourrait également être annulé: rdocumentation.org/search?q=body
Moody_Mudskipper
7

Dans RStudio, il y a (au moins) 3 façons:

  1. Appuyez sur la touche F2 lorsque le curseur est sur n'importe quelle fonction.
  2. Cliquez sur le nom de la fonction tout en maintenant Ctrl ou Commande
  3. View(nom_fonction) (comme indiqué ci-dessus)

Un nouveau volet s'ouvre avec le code source. Si vous atteignez .Primitive ou .C, vous aurez besoin d'une autre méthode, désolé.

Arthur Yip
la source
5

View([function_name])- par exemple. View(mean)Assurez-vous d'utiliser des majuscules [V]. Le code en lecture seule s'ouvrira dans l'éditeur.

Koo
la source
5

Vous pouvez également essayer d'utiliser print.function(), qui est générique S3, pour obtenir l'écriture de la fonction dans la console.

strboul
la source
3
print.function()est une méthode S3 . Le générique est print(). Et ce n'est généralement pas une bonne idée d'appeler directement des méthodes. Cela va à l'encontre de l'objectif des fonctions génériques et de la répartition des méthodes.
Joshua Ulrich