Как я могу просмотреть исходный код функции?

446

Я хочу посмотреть исходный код функции, чтобы увидеть, как она работает. Я знаю, что могу напечатать функцию, набрав ее имя в строке:

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

В этом случае, что означает UseMethod("t")? Как найти исходный код, который фактически используется, например: t(1:10)?

Есть ли разница между тем, когда я вижу UseMethod и когда я вижу standardGeneric и showMethods, как с 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.

В других случаях я вижу, что функции R вызываются, но я не могу найти исходный код для этих функций.

> 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

Как найти такие функции, как .cbindts и .makeNamesTs?

В других случаях есть немного кода R, но большая часть работы, похоже, выполняется где-то еще.

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

Как узнать, что .Primitive функция .Primitive? Кроме того, некоторые функции вызывают .C, .Call, .Fortran, .External или .Internal. Как я могу найти исходный код для них?

Показать ещё 1 комментарий
Теги:
function
r-faq

9 ответов

411
Лучший ответ

UseMethod("t") сообщает вам, что t() является (S3) общей функцией, которая имеет методы для разных классов объектов.

Система отправки S3-метода

Для классов S3 вы можете использовать функцию methods, чтобы перечислить методы для определенной общей функции или класса.

> 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

"Невидимые функции являются звездочками" означает, что функция не экспортируется из пространства имен пакетов. Вы можете просмотреть исходный код с помощью функции ::: (т.е. stats:::t.ts) или с помощью getAnywhere(). getAnywhere() полезен, потому что вам не нужно знать, из какого пакета появилась функция.

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

Система отправки S4

Система S4 представляет собой более новую систему диспетчеризации методов и является альтернативой системе S3. Вот пример функции 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.

Вывод уже содержит много информации. standardGeneric является индикатором функции S4. Метод, чтобы увидеть определенные методы S4, предлагается с пользой:

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

getMethod можно использовать для просмотра исходного кода одного из методов:

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

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

Signatures:
        x               
target  "diagonalMatrix"
defined "diagonalMatrix"

Существуют также методы с более сложными сигнатурами для каждого метода, например

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"

Чтобы увидеть исходный код для одного из этих методов, должна быть поставлена ​​целая подпись, например.

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

Недостаточно предоставить частичную подпись

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

Функции, вызывающие невозбужденные функции

В случае ts.union, .cbindts и .makeNamesTs являются невыполненными функциями из пространства имен stats. Вы можете просмотреть исходный код невыгруженных функций с помощью оператора ::: или 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>

Функции, вызывающие скомпилированный код

Обратите внимание, что "скомпилированный" не ссылается на байт-скомпилированный R-код, созданный пакетом компилятор. Строка <bytecode: 0x294e410> в приведенном выше выводе указывает, что функция скомпилирована в байтах, и вы все еще можете просмотреть источник из командной строки R.

Функции, вызывающие .C, .Call, .Fortran, .External, .Internal или .Primitive, вызывают точки входа в скомпилированном коде, поэтому вам придется искать источники скомпилированного кода если вы хотите полностью понять эту функцию. Это Зеркало GitHub исходного кода R - достойное место для запуска. Функция pryr::show_c_source может быть полезным инструментом, так как она приведет вас непосредственно на страницу GitHub для вызовов .Internal и .Primitive. Пакеты могут использовать .C, .Call, .Fortran и .External; но не .Internal или .Primitive, поскольку они используются для вызова функций, встроенных в интерпретатор R.

Вызов некоторых из вышеперечисленных функций может использовать объект вместо символьной строки для ссылки на скомпилированную функцию. В этих случаях объект имеет класс "NativeSymbolInfo", "RegisteredNativeSymbol" или "NativeSymbol"; и печать объекта дает полезную информацию. Например, optim вызывает .External2(C_optimhess, res$par, fn1, gr1, con) (обратите внимание, что C_optimhess, а не "C_optimhess"). optim находится в пакете статистики, поэтому вы можете ввести stats:::C_optimhess, чтобы просмотреть информацию о вызываемой скомпилированной функции.

Скомпилированный код в пакете

Если вы хотите просмотреть скомпилированный код в пакете, вам необходимо загрузить/распаковать исходный код пакета. Установленных двоичных файлов недостаточно. Исходный код пакета доступен из того же репозитория CRAN (или CRAN-совместимого), из которого первоначально был установлен пакет. Функция download.packages() может получить источник пакета для вас.

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

Это загрузит исходную версию пакета Matrix и сохранит соответствующий файл .tar.gz в текущем каталоге. Исходный код для скомпилированных функций можно найти в каталоге src несжатого и неустановленного файла. Разомкнутый и необратимый шаг можно выполнить вне R или из R с помощью функции untar(). Можно объединить шаг загрузки и расширения в один вызов (обратите внимание, что один и тот же пакет может быть загружен и распакован таким образом):

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

В качестве альтернативы, если разработка пакета общедоступна (например, через GitHub, R-Forge или RForge.net), вы можете, вероятно, просмотреть исходный код онлайн.

Скомпилированный код в базовом пакете

Некоторые пакеты считаются "базовыми" пакетами. Эти пакеты поставляются с R, и их версия блокируется версией R. Примеры включают base, compiler, stats и utils. Таким образом, они недоступны в виде отдельных загружаемых пакетов на CRAN, как описано выше. Скорее, они являются частью дерева источников R в отдельных каталогах пакетов в /src/library/. Как получить доступ к источнику R, описан в следующем разделе.

Скомпилированный код, встроенный в интерпретатор R

Если вы хотите просмотреть встроенный код для интерпретатора R, вам нужно будет загрузить/распаковать источники R; или вы можете просматривать источники в Интернете через R репозиторий Subversion или Winston Chang github зеркало.

Uwe Ligges новостная статья R (PDF) (стр. 43) является хорошей общей ссылкой о том, как просмотреть исходный код для .Internal и .Primitive. Основные шаги - сначала искать имя функции в src/main/names.c, а затем искать имя "C-entry" в файлах в src/main/*.

  • 60
    Если вы используете RStudio , он попытается получить источник для функции, над которой находится текстовый курсор, если вы нажмете клавишу F2 .
  • 1
    @ Ари Б. Фридман Извините за этот поздний вопрос. Будет ли RStudio также получать исходный код C для функции или только для функций, написанных на R? Спасибо
Показать ещё 6 комментариев
79

В дополнение к другим ответам на этот вопрос и его дубликатам, здесь хороший способ получить исходный код для функции пакета без необходимости знать, в какой пакет он находится. например если нам нужен источник для randomForest::rfcv():

Чтобы просмотреть/отредактировать во всплывающем окне:

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

В перенаправление на отдельный файл:

capture.output(getAnywhere('rfcv'), file='source_rfcv.r')
  • 33
    @ downvoters: это на самом деле ценно и приложил усилия, чтобы выяснить. Существует множество причин, по которым вы можете предпочесть отправить длинную функцию с большим количеством логики разбора аргументов в файл, а не на несколько экранов консоли. Я не знал как, поэтому я слез с задницы и понял это. Затем разместил это здесь. Позор вам за подавление.
  • 0
    Следует признать, что getAnywhere - это еще один дурацкий R-выбор имени для чего-то, что должно было называться findOnSearchPath или аналогичным.
Показать ещё 2 комментария
22

Он обнаруживается при отладке с помощью функции debug(). Предположим, вы хотите увидеть базовый код в функции транспонирования t(). Просто набрав 't', не видно много.

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

Но, используя "debug (functionName)", он показывает базовый код, без внутренних элементов.

> 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() выполняет то же самое без использования undebug()

  • 0
    Недостатки этого метода по сравнению с теми, которые приведены в принятом ответе, это то, что вам нужен рабочий вызов функции (все необходимые параметры указаны, приемлемо); и что в дополнение к начальному блоку кода вы также получаете каждый блок во время его выполнения. Это отлично подходит для отладки, но не оптимально для получения исходного кода.
  • 0
    Да, это не оптимально. Но если вы умны, вы можете получить источник быстро и грязно, особенно для встроенных функций.
Показать ещё 1 комментарий
13

Не видел, как это вписывается в поток основного ответа, но меня это застопорило какое-то время, поэтому я добавляю его здесь:

Операторы Infix

Чтобы увидеть исходный код некоторых базовых инфиксных операторов (например, %%, %*%, %in%), используйте getAnywhere, например:

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

В главном ответе описывается, как затем использовать зеркала, чтобы копать глубже.

  • 5
    Ответ getAnywhere рекомендовал getAnywhere . Или вы можете просто использовать обратные пометки, если вы уже знаете имя оператора: `%in%` .
  • 3
    @JoshuaUlrich не знал, что ты можешь использовать обратные трюки! Благодарю. getAnywhere упоминается в вашем ответе, но я думаю, что конкретная ссылка на infix полезна для будущей ссылки на этот ответ - я много раз читал эту страницу и все еще был немного озадачен, пытаясь найти код для таких функций для какое-то время - и я не думал, что это вписывается в поток любого другого ответа (оба используют getAnywhere для другой цели).
11

Для непримитивных функций база R включает в себя функцию с именем body(), которая возвращает тело функции. Например, источник функции print.Date() можно просмотреть:

body(print.Date)

произведет это:

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

Если вы работаете в script и хотите, чтобы код функции был символьным символом, вы можете получить его.

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

вы получите:

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

Зачем мне это делать? Я создал пользовательский объект S3 (x, где class(x) = "foo") на основе списка. Один из участников списка (называемый "fun" ) был функцией, и я хотел, чтобы print.foo() отображал исходный код функции с отступом. Таким образом, я получил следующий фрагмент в print.foo():

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

который отступы и отображает код, связанный с x[["fun"]].

6

В R edit

есть очень удобная функция,
new_optim <- edit(optim)

Он откроет исходный код optim, используя редактор, указанный в R options, а затем вы можете отредактировать его и назначить измененной функции new_optim. Мне очень нравится эта функция, чтобы просматривать код или отлаживать код, например, печатать некоторые сообщения или переменные или даже назначать их глобальным переменным для дальнейшего исследования (конечно, вы можете использовать debug).

Если вы просто хотите просмотреть исходный код и не хотите, чтобы на консоль был напечатан раздражающий длинный исходный код, вы можете использовать

invisible(edit(optim))

Ясно, что это невозможно использовать для просмотра исходного кода C/С++ или Fortran.

BTW, edit могут открывать другие объекты, такие как список, матрица и т.д., которые также показывают структуру данных с атрибутами. Функция de может быть использована для открытия редактора Excel (если он поддерживает GUI) для изменения матрицы или кадра данных и возврата нового. Иногда это удобно, но его следует избегать в обычном случае, особенно когда вы крупны.

  • 3
    Этот подход вызывает только тот же источник функции, который дает печать функции (то есть то же, что и в вопросе). Вопрос в том, о чем идет дальше / глубже.
  • 2
    @BrianDiggs Да, вы правы. Я не хотел давать ответ на вопрос, поскольку Иисус Навин дал довольно полный ответ. Я просто пытаюсь добавить что-то связанное с темой, интересное и, возможно, полезное знать.
3

Пока функция написана в чистом R не C/С++/Fortran, можно использовать следующее. В противном случае наилучшим способом является отладка и использование " jump в":

> functionBody(functionName)
  • 2
    Это так же, как body . identical(functionBody, body) TRUE .
  • 0
    base::body и methods::functionBody , хотя они непривычны для отсоединения. body может быть переопределено: rdocumentation.org/search?q=body
2

Вы также можете попробовать использовать print.function(), который является S3 generic, чтобы функция записывалась в консоль.

  • 1
    print.function() - это метод S3. Универсальным является print() . И вообще не стоит вызывать методы напрямую. Это наносит ущерб всей цели универсальных функций и методов отправки.
2

View([function_name]) - например. View(mean) Обязательно используйте прописные буквы [V]. Код только для чтения откроется в редакторе.

  • 12
    Это работает только в Rstudio.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню