web-dev-qa-db-fra.com

Comment visualiser le code source d'une fonction?

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, que signifie UseMethod("t")? Comment trouver le code source réellement utilisé par, par exemple: t(1:10)?

Existe-t-il une différence entre quand je vois UseMethod et quand je vois standardGeneric et 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 .cbindts et .makeNamesTs?

Dans d'autres cas encore, il y a un peu de code R, mais la majeure partie du travail semble être effectuée 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 fonction .Primitive? De même, certaines fonctions appellent .C, .Call, .Fortran, .External ou .Internal. Comment puis-je trouver le code source pour ceux?

491
Joshua Ulrich

UseMethod("t") vous dit que t() est une fonction générique ( S ) qui comporte des méthodes pour différentes classes d'objets.

Le système d'envoi de méthode S3

Pour les classes S3, vous pouvez utiliser la fonction methods pour répertorier les méthodes d'une fonction ou d'une 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 à partir de l'espace de noms de son package. Vous pouvez toujours afficher son code source via la fonction ::: (c'est-à-dire 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 d'envoi de méthode S4

Le système S4 ​​est un système de répartition de méthode plus récent et constitue 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. standardGeneric est un indicateur d'une fonction S4. La méthode pour voir les méthodes S4 définies est offerte de manière utile:

> 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 d'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, vous devez fournir la signature complète, 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, .cbindts et .makeNamesTs sont des fonctions non exportées de l'espace de noms stats. Vous pouvez afficher le code source des fonctions non exportées à l’aide de l’opérateur ::: ou de 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 ligne <bytecode: 0x294e410> dans la sortie ci-dessus indique que la fonction est compilée sur des octets et que vous pouvez toujours afficher la source à partir de la ligne de commande R.

Les fonctions qui appellent .C, .Call, .Fortran, .External, .Internal ou .Primitive appellent des points d’entrée sous forme de code compilé. Il faut regarder les sources du code compilé si on veut bien comprendre la fonction. This Le miroir GitHub du code source R est un bon endroit pour commencer. La fonction pryr::show_c_source peut être un outil utile car elle vous mènera directement à une page GitHub pour les appels .Internal et .Primitive. Les packages peuvent utiliser .C, .Call, .Fortran et .External; mais pas .Internal ni .Primitive, car ils sont utilisés pour appeler des fonctions intégrées dans 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 faire référence à la fonction compilée. Dans ces cas, l'objet est de classe "NativeSymbolInfo", "RegisteredNativeSymbol" ou "NativeSymbol"; et imprimer l'objet donne des informations utiles. Par exemple, optim appelle .External2(C_optimhess, res$par, fn1, gr1, con) (notez que C_optimhess, et non "C_optimhess"). optim est dans le paquet stats, vous pouvez donc taper stats:::C_optimhess pour afficher des informations sur la fonction compilée appelée.

Code compilé dans un package

Si vous souhaitez afficher le code compilé dans un package, vous devez télécharger/décompresser la source du package. Les fichiers 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é installé à l'origine. La fonction download.packages() peut obtenir la source du paquet pour vous.

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

Cela téléchargera la version source du paquet Matrix et enregistrera le fichier .tar.gz correspondant dans le répertoire en cours. Le code source des fonctions compilées se trouve dans le répertoire src du fichier non compressé et non décompressé. L'étape de décompression et de décompression peut être effectuée en dehors de R ou de l'intérieur de R à l'aide de la fonction untar(). Il est possible de combiner l’étape de téléchargement et d’extension en un seul appel (notez qu’un package à la fois peut être téléchargé et décompressé de cette manière):

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

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

Code compilé dans un package de base

Certains paquets sont considérés comme des paquets "de base". Ces packages sont livrés avec R et leur version est verrouillée à la version de R. Les exemples incluent base, compiler, stats et utils. En tant que tels, ils ne sont pas disponibles sous forme de packages téléchargeables distincts sur CRAN, comme décrit ci-dessus. Ils font plutôt partie de l'arborescence source R dans des 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 devez télécharger/décompresser les sources R; ou vous pouvez visualiser les sources en ligne via le R référentiel Subversion ou le miroir github de Winston Chang .

Uwe Ligges Article de presse R (PDF) (p. 43) est une bonne référence générale sur la façon de visualiser le code source pour les fonctions .Internal et .Primitive. La procédure de base consiste tout d'abord à rechercher le nom de la fonction dans src/main/names.c, puis à rechercher le nom "Entrée C" dans les fichiers de src/main/*.

472
Joshua Ulrich

Outre les 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 il se trouve. si nous voulons la source pour randomForest::rfcv():

Pour voir/éditer dans une fenêtre pop-up:

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

Pour rediriger vers un fichier séparé:

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

Il est révélé lorsque vous déboguez en utilisant la fonction debug (). Supposons que vous souhaitiez voir le code sous-jacent dans la fonction t() transpose. Juste taper 't', ne révèle pas beaucoup.

>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 éléments 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 ()

25
Selva

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 fonction print.Date() peut être visualisé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 voulez que le code de la 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 chose pareille? 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 que print.foo() affiche le code source de la fonction, en retrait. Je me suis donc retrouvé avec l'extrait suivant dans print.foo():

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

qui met en retrait et affiche le code associé à x[["fun"]].

19
Geoffrey Poole

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

Infix Opérateurs

Pour voir le code source de certains opérateurs infixes de base (par exemple, %%, %*%, %in%), utilisez 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 porte sur la manière d'utiliser ensuite les miroirs pour creuser plus profondément.

18
MichaelChirico

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

new_optim <- edit(optim)

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

Si vous voulez juste voir le code source et que vous ne voulez pas que le long code source soit ennuyeux imprimé sur votre console, vous pouvez utiliser

invisible(edit(optim))

Il est évident que cela ne peut pas être utilisé pour afficher le code source C/C++ ou Fortran.

BTW, edit peut ouvrir d'autres objets tels que liste, matrice, etc., qui affiche ensuite la structure de données avec les attributs également. La fonction de peut être utilisée pour ouvrir un Excel comme éditeur (si l’interface graphique le permet) pour modifier une matrice ou un cadre de données et renvoyer le nouveau. C'est pratique parfois, mais devrait être évité dans les cas habituels, surtout quand votre matrice est grosse.

9
Eric

Tant que la fonction est écrite en R pur et non en C/C++/Fortran, on peut utiliser ce qui suit. Sinon, le meilleur moyen est débogage et en utilisant "sauten":

> functionBody(functionName)
7
MCH

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

5
strboul

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

4
Koo

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

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

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

3
Arthur Yip