Wie kann ich den Quellcode für eine Funktion anzeigen?

551

Ich möchte im Quellcode nach einer Funktion suchen, um zu sehen, wie sie funktioniert. Ich weiß, dass ich eine Funktion drucken kann, indem ich ihren Namen an der Eingabeaufforderung eingebe:

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

Was bedeutet in diesem Fall UseMethod("t")? Wie finde ich den Quellcode, der tatsächlich verwendet wird, zum Beispiel : t(1:10)?

Gibt es einen Unterschied zwischen wann ich sehe UseMethodund wann ich sehe standardGenericund showMethodswie bei 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 anderen Fällen kann ich sehen, dass R-Funktionen aufgerufen werden, aber ich kann den Quellcode für diese Funktionen nicht finden.

> 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

Wie finde ich Funktionen wie .cbindtsund .makeNamesTs?

In noch anderen Fällen gibt es ein bisschen R-Code, aber die meiste Arbeit scheint woanders erledigt zu sein.

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

Wie finde ich heraus, was die .PrimitiveFunktion tut? In ähnlicher Weise einige Funktionen aufrufen .C, .Call, .Fortran, .External, oder .Internal. Wie finde ich den Quellcode für diese?

Joshua Ulrich
quelle
2
Siehe auch stackoverflow.com/q/1439348/134830
Richie Cotton

Antworten:

518

UseMethod("t")sagt Ihnen, dass t()es sich um eine ( S3 ) generische Funktion handelt, die Methoden für verschiedene Objektklassen enthält.

Das Versandverfahren nach der S3-Methode

Für S3-Klassen können Sie die methodsFunktion verwenden, um die Methoden für eine bestimmte generische Funktion oder Klasse aufzulisten.

> 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

"Nicht sichtbare Funktionen sind mit einem Sternchen versehen" bedeutet, dass die Funktion nicht aus dem Namespace ihres Pakets exportiert wird. Sie können den Quellcode weiterhin über die :::Funktion (dh stats:::t.ts) oder mithilfe von anzeigen getAnywhere(). getAnywhere()ist nützlich, weil Sie nicht wissen müssen, von welchem ​​Paket die Funktion stammt.

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

Das S4-Methodenversandsystem

Das S4-System ist ein neueres Methodenversandsystem und eine Alternative zum S3-System. Hier ist ein Beispiel für eine S4-Funktion:

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

Die Ausgabe bietet bereits viele Informationen. standardGenericist ein Indikator für eine S4-Funktion. Die Methode zum Anzeigen definierter S4-Methoden wird hilfreich angeboten:

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

getMethod kann verwendet werden, um den Quellcode einer der folgenden Methoden anzuzeigen:

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

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

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

Es gibt beispielsweise auch Methoden mit komplexeren Signaturen für jede Methode

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"

Um den Quellcode für eine dieser Methoden anzuzeigen, muss die gesamte Signatur bereitgestellt werden, z

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

Es reicht nicht aus, die Teilunterschrift zu liefern

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

Funktionen, die nicht exportierte Funktionen aufrufen

Im Fall von ts.union, .cbindtsund .makeNamesTssind unexported Funktionen aus dem statsNamespace. Sie können den Quellcode nicht exportierter Funktionen mit dem :::Operator oder anzeigen 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>

Funktionen, die kompilierten Code aufrufen

Beachten Sie, dass "kompiliert" nicht auf bytekompilierten R-Code verweist, wie er vom Compilerpaket erstellt wurde. Die <bytecode: 0x294e410>Zeile in der obigen Ausgabe zeigt an, dass die Funktion bytekompiliert ist und Sie die Quelle weiterhin über die R-Befehlszeile anzeigen können.

Funktionen dieser Anruf .C, .Call, .Fortran, .External, .Internal, oder .PrimitiveEinstiegspunkte in kompilierten Code aufrufen, so dass Sie auf Quellen des kompilierten Codes suchen müssen, wenn Sie die Funktion vollständig verstehen wollen. Dieser GitHub-Spiegel des R-Quellcodes ist ein guter Ausgangspunkt. Die Funktion pryr::show_c_sourcekann ein nützliches Werkzeug sein, da sie Sie direkt zu einer GitHub-Seite für .Internalund .PrimitiveAufrufen führt. Pakete können verwendet werden .C, .Call, .Fortranund .External; aber nicht .Internaloder .Primitive, weil diese verwendet werden, um Funktionen aufzurufen, die in den R-Interpreter eingebaut sind.

Aufrufe einiger der oben genannten Funktionen können ein Objekt anstelle einer Zeichenfolge verwenden, um auf die kompilierte Funktion zu verweisen. In diesen Fällen ist das Objekt der Klasse "NativeSymbolInfo", "RegisteredNativeSymbol"oder "NativeSymbol"; und das Drucken des Objekts liefert nützliche Informationen. Zum Beispiel optimAnrufe .External2(C_optimhess, res$par, fn1, gr1, con)(beachten Sie, dass dies nicht der C_optimhessFall ist "C_optimhess"). optimbefindet sich im Statistikpaket, sodass Sie eingeben können stats:::C_optimhess, um Informationen zur aufgerufenen kompilierten Funktion anzuzeigen.

Kompilierter Code in einem Paket

Wenn Sie kompilierten Code in einem Paket anzeigen möchten, müssen Sie die Paketquelle herunterladen / entpacken. Die installierten Binärdateien reichen nicht aus. Der Quellcode eines Pakets ist im selben CRAN-Repository (oder CRAN-kompatiblen Repository) verfügbar, aus dem das Paket ursprünglich installiert wurde. Die download.packages()Funktion kann die Paketquelle für Sie abrufen.

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

Dadurch wird die Quellversion des Matrix-Pakets heruntergeladen und die entsprechende .tar.gzDatei im aktuellen Verzeichnis gespeichert. Der Quellcode für kompilierte Funktionen befindet sich im srcVerzeichnis der unkomprimierten und nicht tarierten Datei. Der Dekomprimierungs- und Entpackungsschritt kann außerhalb Roder von innen Rmithilfe der untar()Funktion ausgeführt werden. Es ist möglich, den Download- und Erweiterungsschritt in einem einzigen Aufruf zu kombinieren (beachten Sie, dass auf diese Weise jeweils nur ein Paket heruntergeladen und entpackt werden kann):

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

Wenn die Paketentwicklung öffentlich gehostet wird (z. B. über GitHub , R-Forge oder RForge.net ), können Sie den Quellcode wahrscheinlich auch online durchsuchen.

Kompilierter Code in einem Basispaket

Bestimmte Pakete gelten als "Basis" -Pakete. Diese Pakete versenden mit R und deren Version auf die Version von R. Beispiele gesperrt sind base, compiler, stats, und utils. Daher sind sie nicht wie oben beschrieben als separate herunterladbare Pakete auf CRAN verfügbar. Sie sind vielmehr Teil des R-Quellbaums in einzelnen Paketverzeichnissen unter /src/library/. Der Zugriff auf die R-Quelle wird im nächsten Abschnitt beschrieben.

In den R-Interpreter integrierter kompilierter Code

Wenn Sie den im R-Interpreter integrierten Code anzeigen möchten, müssen Sie die R-Quellen herunterladen / entpacken. Oder Sie können die Quellen online über das R Subversion-Repository oder den Github-Spiegel von Winston Chang anzeigen .

Der R-Nachrichtenartikel (PDF) von Uwe Ligges (S. 43) ist eine gute allgemeine Referenz zum Anzeigen des Quellcodes .Internalund der .PrimitiveFunktionen. Die grundlegenden Schritte bestehen darin, zuerst nach dem Funktionsnamen in zu src/main/names.csuchen und dann in den Dateien in nach dem Namen "C-Eintrag" zu suchen src/main/*.

Joshua Ulrich
quelle
71
Wenn Sie verwenden RStudio, wird versucht, die Quelle für die Funktion zu ziehen, bei der sich Ihr Textcursor befindet, wenn Sie die F2Taste drücken.
Ari B. Friedman
1
@ Ari B. Friedman Entschuldigung für diese späte Frage. Wird RStudio auch den C-Quellcode für die Funktion oder nur für die in R geschriebenen Funktionen abrufen? Vielen Dank
Sunny
3
@ Samir Ich glaube, es ist nur die R-Quelle.
Ari B. Friedman
@ AriB.Friedman - danke Ari, das ist praktisch. In meinem Fall brauchte ich noch das in der Antwort gezeigte Wissen ( scaledas ist S3 - ich habe es bekommen UseMethod("scale")und dann benutzt getAnywhere(scale.default)). Aber einfache Funktionen funktionieren einwandfrei.
Tomasz Gandor
2
Nachahmung ist die aufrichtigste Form der Schmeichelei. Ich gehe davon aus, dass diese Antwort / dieses Wiki an erster Stelle stand :) Vor dieser rfaqs.com/source-code-of-r-method
JimLohse
94

Zusätzlich zu den anderen Antworten auf diese Frage und ihre Duplikate ist hier eine gute Möglichkeit, den Quellcode für eine Paketfunktion abzurufen, ohne wissen zu müssen, in welchem ​​Paket sie sich befindet. Beispiel: Wenn wir die Quelle für Folgendes benötigen randomForest::rfcv():

So zeigen Sie es in einem Popup-Fenster an / bearbeiten es:

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

So leiten Sie in eine separate Datei um :

capture.output(getAnywhere('rfcv'), file='source_rfcv.r')
smci
quelle
Zugegeben, getAnywhere ist eine weitere verrückte R-Namenswahl für etwas, das findOnSearchPath oder ähnliches hätte heißen sollen .
smci
1
Ich werde diese Antwort positiv bewerten, weil sie mich dem nahe gebracht hat, was ich wollte. Was ich eigentlich in RStudio wollte, war View(foo); Wo foowar eine Funktion aus einem bereits geladenen Paket.
Sigfried
1
@Sigfried: Öffnet edit()einen Texteditor (nach Wahl des Benutzers) , während View()ein Tabellenkalkulations-Viewer vom Typ Excel für Daten geöffnet wird. Letzterer eignet sich zum Durchsuchen von (mehrspaltigen) Daten, ist jedoch normalerweise für Code mit einer anderen Länge als Spielzeug schrecklich. Wie ich zum Beispiel anspreche, ist das erste, was ich beim Durchsuchen einer Funktion tun möchte, das Überspringen / Reduzieren / Dummy aller Arg-Parsing- und Standard-Aktionslogik, um zu sehen, was die Funktion tatsächlich tut .
smci
25

Es wird angezeigt, wenn Sie mit der Funktion debug () debuggen. Angenommen, Sie möchten den zugrunde liegenden Code in der Transponierungsfunktion t () sehen. Nur 't' zu tippen, verrät nicht viel.

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

Bei Verwendung von 'debug (functionName)' wird jedoch der zugrunde liegende Code ohne Interna angezeigt.

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

BEARBEITEN : debugonce () erreicht dasselbe, ohne undebug () verwenden zu müssen

Selva
quelle
Die Nachteile dieser Methode im Vergleich zu denen in der akzeptierten Antwort sind, dass Sie einen funktionierenden Funktionsaufruf benötigen (alle erforderlichen Parameter angegeben, akzeptabel); und dass Sie zusätzlich zum anfänglichen Codeblock auch jeden Block zum Zeitpunkt seiner Ausführung erhalten. Dies ist ideal zum Debuggen, aber nicht optimal, um nur die Quelle abzurufen.
Brian Diggs
Ja, es ist nicht optimal. Aber wenn Sie klug sind, können Sie die Quelle schnell und schmutzig bekommen, besonders für eingebaute Funktionen.
Selva
2
Ich würde auch empfehlen, debugonceanstelle von debugin diesem Fall zu verwenden.
Joshua Ulrich
20

Für nicht-primitive Funktionen enthält R base eine Funktion namens body(), die den Funktionskörper zurückgibt. Zum Beispiel kann die Quelle der print.Date()Funktion angezeigt werden:

body(print.Date)

wird dies produzieren:

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

Wenn Sie in einem Skript arbeiten und den Funktionscode als Zeichenvektor verwenden möchten, können Sie ihn abrufen.

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

Kriege dich:

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

Warum sollte ich so etwas tun wollen? Ich habe ein benutzerdefiniertes S3-Objekt ( x, wo class(x) = "foo") basierend auf einer Liste erstellt. Eines der Listenmitglieder (mit dem Namen "fun") war eine Funktion, und ich wollte print.foo()den eingerückten Funktionsquellcode anzeigen. So kam ich zu folgendem Ausschnitt print.foo():

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

Hiermit wird der zugeordnete Code eingerückt und angezeigt x[["fun"]].

Geoffrey Poole
quelle
18

Ich habe nicht gesehen, wie dies in den Fluss der Hauptantwort passt, aber es hat mich eine Weile verblüfft, also füge ich es hier hinzu:

Infix-Operatoren

Um zu sehen, den Quellcode einiger Basis Infixoperatoren (zB %%, %*%, %in%), verwenden getAnywhere, zum Beispiel:

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

Die Hauptantwort beschreibt, wie man dann Spiegel verwendet, um tiefer zu graben.

MichaelChirico
quelle
6
Die Antwort von smci wird empfohlen getAnywhere. Oder Sie können einfach Backticks verwenden, wenn Sie den Namen des Operators bereits kennen : `%in%`.
Joshua Ulrich
3
@JoshuaUlrich wusste nicht, dass Sie Backticks verwenden können! Vielen Dank. getAnywherewird auch in Ihrer Antwort erwähnt, aber ich denke, ein spezifischer Verweis auf Infix ist nützlich für zukünftige Verweise auf diese Antwort - ich habe diese Seite oft gelesen und war immer noch etwas ratlos, als ich versuchte, Code für solche Funktionen für a zu finden während - und ich dachte nicht, dass es in den Fluss einer der anderen Antworten passt (die beide getAnywherefür einen anderen Zweck verwenden).
MichaelChirico
10

Es gibt eine sehr praktische Funktion in R. edit

new_optim <- edit(optim)

Es öffnet den Quellcode der optimVerwendung des in Rs angegebenen Editors options, und dann können Sie ihn bearbeiten und die geänderte Funktion zuweisen new_optim. Ich mag diese Funktion sehr, um Code anzuzeigen oder den Code zu debuggen, z. B. einige Nachrichten oder Variablen zu drucken oder sie sogar globalen Variablen zur weiteren Untersuchung zuzuweisen (natürlich können Sie sie verwendendebug ).

Wenn Sie nur den Quellcode anzeigen möchten und nicht möchten, dass der nervige lange Quellcode auf Ihrer Konsole gedruckt wird, können Sie ihn verwenden

invisible(edit(optim))

Dies kann natürlich nicht zum Anzeigen von C / C ++ - oder Fortran-Quellcode verwendet werden.

Übrigens, edit kann andere Objekte wie Liste, Matrix usw. öffnen, die dann auch die Datenstruktur mit Attributen anzeigen. Mit dieser Funktion dekönnen Sie einen Excel-ähnlichen Editor öffnen (sofern die GUI dies unterstützt), um die Matrix oder den Datenrahmen zu ändern und den neuen zurückzugeben. Dies ist manchmal praktisch, sollte aber im Normalfall vermieden werden, insbesondere wenn Ihre Matrix groß ist.

Eric
quelle
3
Bei diesem Ansatz wird nur dieselbe Funktionsquelle aufgerufen, die beim Drucken der Funktion angegeben wird (dh dieselbe wie in der Frage). Weiter / tiefer zu gehen, darum geht es in dieser Frage.
Brian Diggs
2
@ BrianDiggs Ja, du hast recht. Ich wollte keine Antwort auf die Frage geben, da Joshua eine ziemlich vollständige Antwort gegeben hat. Ich versuche nur, etwas zum Thema hinzuzufügen, das interessant und nützlich sein kann.
Eric
8

Solange die Funktion in reinem R und nicht in C / C ++ / Fortran geschrieben ist, kann man Folgendes verwenden. Ansonsten ist der beste Weg das Debuggen und Verwenden von " Sprung in ":

> functionBody(functionName)
MCH
quelle
2
Dies ist das gleiche wie body. identical(functionBody, body)ist TRUE.
Joshua Ulrich
1
base::bodyund methods::functionBodyobwohl sie nicht in der Lage sind, enttäuscht zu werden. bodykönnte auch überschrieben werden: rdocumentation.org/search?q=body
Moody_Mudskipper
7

In RStudio gibt es (mindestens) drei Möglichkeiten:

  1. Drücken Sie die Taste F2, während sich der Cursor auf einer beliebigen Funktion befindet.
  2. Klicken Sie auf den Funktionsnamen, während Sie Strg oder Befehl gedrückt halten
  3. View(Funktionsname) (wie oben angegeben)

Ein neuer Bereich mit dem Quellcode wird geöffnet. Wenn Sie .Primitive oder .C erreichen, benötigen Sie leider eine andere Methode.

Arthur Yip
quelle
5

View([function_name])- z.B. View(mean)Stellen Sie sicher, dass Sie Großbuchstaben [V] verwenden. Der schreibgeschützte Code wird im Editor geöffnet.

Koo
quelle
5

Sie können auch versuchen print.function(), S3 generisch zu verwenden, um die Funktion in die Konsole zu schreiben.

strboul
quelle
3
print.function()ist eine S3- Methode . Das Generikum ist print(). Und es ist im Allgemeinen keine gute Idee, Methoden direkt aufzurufen. Das macht den gesamten Zweck der generischen Funktionen und des Methodenversands zunichte.
Joshua Ulrich