Як я можу переглянути вихідний код функції?


550

Я хочу подивитися вихідний код функції, щоб побачити, як вона працює. Я знаю, що я можу надрукувати функцію, ввівши її ім'я під запитом:

> 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робить функція? Крім того , деякі функції називають .C, .Call, .Fortran, .External, або .Internal. Як я можу знайти вихідний код для них?




Відповіді:


518

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 або дзеркало github Winston Chang .

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


71
Якщо ви використовуєте RStudio, він спробує витягнути джерело для функції, над якою закінчується текстовий курсор, якщо натиснути F2клавішу.
Арі Б. Фрідман

1
@Ari B. Friedman Вибачте за це пізнє запитання. Чи RStudio також потягне вихідний код C для функції або просто для функцій, написаних на R? Спасибі
Сонячний

3
@Samir Я вважаю, що це просто джерело R.
Арі Б. Фрідман

@ AriB.Friedman - дякую Арі, це зручно. У моєму випадку мені ще потрібні знання, показані у відповіді ( scaleа це S3 - я отримав UseMethod("scale")і потім використав getAnywhere(scale.default)). Але звичайні функції працюють чудово.
Томаш Гандор

2
Імітація - це найщиріша форма лестощів. Я припускаю, що ця відповідь / wiki прийшла першою :) Перед цим rfaqs.com/source-code-of-r-method
JimLohse

94

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

Щоб переглянути / відредагувати його у спливаючому вікні:

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

Для переадресації в окремий файл :

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

Правда, getAnywhere - це ще один нерозумний вибір імені R для того, що повинно було називатися findOnSearchPath чи подібним.
smci

1
Я підтримаю цю відповідь, тому що вона наблизила мене до того, що я хотів. Те, що я насправді хотів, в RStudio, було View(foo); де fooбула функція з уже завантаженого пакету.
Зігфрід

1
@Sigfried: edit()відкриває текстовий редактор (за вибором користувача) , тоді як View()відкриває програму перегляду електронних таблиць типу Excel для даних , остання корисна для перегляду даних (багато стовпчикових), але зазвичай жахлива для коду будь-якого, крім довжини іграшки. Наприклад, як я натякаю, як правило, перше, що я хочу зробити під час перегляду функції, - пропустити / згорнути / манекен, винести всю логіку розбору аргументів та логіку дій за замовчуванням, щоб побачити, що ця функція насправді виконує .
smci

25

Він виявляється при налагодженні за допомогою функції 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 ()


Мінуси цього методу порівняно з тими, що наведені у прийнятій відповіді, полягають у тому, що вам потрібен виклик робочої функції (всі необхідні параметри вказані, прийнятно); і що, крім початкового блоку коду, ви також отримуєте кожен блок під час його запуску. Це чудово для налагодження, але не оптимально для отримання лише джерела.
Брайан Діггс

Так, це не оптимально. Але якщо ви розумні, ви можете отримати джерело швидким і брудним, особливо для вбудованих функцій.
Сельва

2
Я також рекомендую використовувати debugonceзамість debugцього випадку.
Джошуа Ульріх

20

Для непомітивних функцій база 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)
}

Якщо ви працюєте в сценарії і хочете код функції як векторного символу, ви можете отримати його.

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") на основі списку. Один із членів списку (названий "весело") був функцією, і я хотів print.foo()відобразити вихідний код функції з відступом. Тож я опинив такий фрагмент у print.foo():

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

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


18

Я не бачив, як це вписується в потік основної відповіді, але це натякало мене на деякий час, тому я додаю його тут:

Оператори 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("%%")

Основна відповідь стосується того, як потім використовувати дзеркала для глибшого копання.


6
Відповідь smci рекомендується getAnywhere. Або ви могли б просто використовувати лапки , якщо ви вже знаєте ім'я оператора: `%in%`.
Джошуа Ульріх

3
@JoshuaUlrich не знав, що ти можеш використовувати зворотній зв'язок! Дякую. getAnywhereзгадується і у вашій відповіді, але я думаю, що конкретна посилання на інфікс корисна для подальшої посилання на цю відповідь - я читав цю сторінку багато разів і все ще був здивований, намагаючись знайти код для таких функцій для в той час - і я не вважав, що це вписується в потік будь-якої іншої відповіді (яку обидва використовують getAnywhereз іншою метою).
MichaelChirico

10

У R є дуже зручна функція edit

new_optim <- edit(optim)

Він відкриє вихідний код optimвикористання редактора, вказаного в R's options, а потім ви можете відредагувати його та призначити модифіковану функцію new_optim. Мені дуже подобається ця функція для перегляду коду або налагодження коду, наприклад, надрукувати деякі повідомлення чи змінні або навіть призначити їх глобальним змінним для подальшого дослідження (звичайно, ви можете використовувати debug).

Якщо ви просто хочете переглянути вихідний код і не хочете, щоб набридлий довгий вихідний код був надрукований на вашій консолі, ви можете використовувати

invisible(edit(optim))

Зрозуміло, це не можна використовувати для перегляду C / C ++ або вихідного коду Fortran.

BTW editможе відкривати інші об'єкти, такі як список, матриця тощо, де потім відображається структура даних з атрибутами. Функція deможе бути використана для відкриття редактора на зразок excel (якщо GUI підтримує його) для зміни матриці або кадру даних та повернення нового. Іноді це зручно, але цього слід уникати у звичайних випадках, особливо коли матриця велика.


3
Цей підхід відображає лише те саме джерело функції, яке дає функція друку (тобто таке, як у запитанні). Подальше / глибше, ніж це, в чому полягає це питання.
Брайан Діггс

2
@BrianDiggs Так, ви праві. Я не хотів давати відповідь на запитання, оскільки Джошуа дав цілком повну відповідь. Я просто намагаюся додати щось, що стосується теми, цікавого і може бути корисним знати.
Ерік

8

Поки функція записана в чистому R, а не C / C ++ / Fortran, можна використовувати наступне. Інакше найкращий спосіб - це налагодження та використання " стрибати в ":

> functionBody(functionName)

2
Це те саме, що body. identical(functionBody, body)є TRUE.
Джошуа Ульріх

1
base::bodyі methods::functionBody, хоча вони навряд чи будуть відсторонені. bodyможе бути також відмінено: rdocumentation.org/search?q=body
Moody_Mudskipper

7

У RStudio є (принаймні) 3 способи:

  1. Натисніть клавішу F2, поки курсор увімкнено для будь-якої функції.
  2. Клацніть ім'я функції, утримуючи Ctrl або Command
  3. View(ім'я функції) (як зазначено вище)

Відкриється нова панель із вихідним кодом. Якщо ви досягнете .Primitive або .C, вам потрібен інший метод, вибачте.


5

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


5

Ви також можете спробувати використовувати print.function(), що є загальним для S3, для отримання функції запису в консолі.


3
print.function()є методом S3 . Родовий є print(). І взагалі не годиться безпосередньо називати методи. Це перемагає всю мету загальних функцій та методу відправки.
Джошуа Ульріх
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.