함수의 소스 코드를 어떻게 볼 수 있습니까?


551

작동 방식을 확인하는 함수의 소스 코드를보고 싶습니다. 프롬프트에서 이름을 입력하여 함수를 인쇄 할 수 있다는 것을 알고 있습니다.

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

이 경우 무엇을 UseMethod("t")의미합니까? 실제로 사용되고있는 소스 코드를 어떻게 찾 t(1:10)습니까?

내가 볼 때 차이가 있습니까 UseMethod내가 볼 때 standardGenericshowMethods와 마찬가지로는 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.

출력은 이미 많은 정보를 제공합니다. standardGenericS4 기능의 표시기입니다. 정의 된 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로부터 안 export 기능입니다 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완전히 기능을 이해하려는 경우 컴파일 된 코드의 소스를 보는 것, 그래서 컴파일 된 코드의 진입 점을 요구하고있다. R 소스 코드의 GitHub 미러는 시작하기에 적당한 곳입니다. 이 기능은 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는 압축되지 않은 파일과 압축 해제 된 파일 의 디렉토리 에서 찾을 수 있습니다 . 압축 해제 및 untaring 단계는 외부 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) (p. 43)는 소스 코드 .Internal.Primitive기능 을 보는 방법에 대한 일반적인 참고 자료입니다 . 기본 단계는 먼저 함수 이름을 src/main/names.c찾은 다음의 파일에서 "C-entry"이름을 검색하는 것입니다 src/main/*.


71
를 사용 RStudio하는 경우 F2키 를 누르면 텍스트 커서가있는 기능의 소스를 가져 오려고 시도합니다 .
Ari B. Friedman

1
@Ari B. Friedman이 늦은 질문에 대해 죄송합니다. RStudio는 함수 또는 R로 작성된 함수의 C 소스 코드도 가져 옵니까? 감사합니다
Sunny

3
@ 사미르 나는 그것이 단지 R 소스라고 생각합니다.
Ari B. Friedman

@ AriB.Friedman-감사합니다 Ari, 이것은 편리합니다. 내 경우에는 여전히 대답에 표시된 지식이 필요했습니다 ( scaleS3-얻었고 UseMethod("scale")사용했습니다 getAnywhere(scale.default)). 그러나 평범한 기능은 잘 작동합니다.
Tomasz Gandor

2
모조 나는이 대답을 가정 아첨의 충심으로 양식입니다 / 위키 먼저 온 :이 전에 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')

틀림없이 getAnywherefindOnSearchPath 또는 이와 유사한 이름을 가진 또 다른 별난 이름입니다 .
smci

1
이 답변이 내가 원하는 것에 가깝게 되었기 때문에이 답변을지지 할 것입니다. 내가 실제로 원했던 것은 RStudio에서 View(foo); foo이미로드 된 패키지의 함수가 어디에 있습니까 ?
Sigfried

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)

편집 : debugonce ()는 undebug ()를 사용하지 않고도 동일한 결과를 얻습니다.


수락 된 답변에 제공된 것과 비교 하여이 방법의 단점은 작업 함수 호출이 필요하다는 것입니다 (필요한 모든 필수 매개 변수가 지정 가능함). 그리고 코드의 초기 블록 외에도 각 블록이 실행될 때 얻을 수 있습니다. 이것은 디버깅에는 좋지만 소스를 얻는 데 최적은 아닙니다.
Brian Diggs

예, 최적이 아닙니다. 그러나 당신이 영리하다면, 내장 된 기능을 위해 소스를 빠르고 더럽힐 수 있습니다.
셀바

2
이 인스턴스 debugonce대신에 사용 하는 것이 좋습니다 debug.
Joshua Ulrich

20

기본이 아닌 함수의 경우 R base에는 함수 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, where class(x) = "foo")를 작성했습니다. "fun"이라는 목록 멤버 중 하나가 함수였으며 print.foo()들여 쓰기 된 함수 소스 코드를 표시 하려고 했습니다. 그래서 나는 다음 스 니펫으로 끝났습니다 print.foo().

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

에 연결된 코드를 들여 쓰고 표시합니다 x[["fun"]].


18

이것이 주요 답변의 흐름에 어떻게 적합한 지 보지 못했지만 잠시 동안 저를 비틀어 여기에 추가하고 있습니다.

대입 연산자

일부 기본 중위 사업자 (예를 들어,의 소스 코드를 확인하려면 %%, %*%, %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%`.
Joshua Ulrich

3
@JoshuaUlrich는 백틱을 사용할 수 있다는 것을 몰랐습니다! 감사. getAnywhere귀하의 답변에도 언급되어 있지만, infix에 대한 특정 참조는이 답변에 대한 향후 참조에 유용하다고 생각합니다.이 페이지를 여러 번 읽었으며 여전히 이러한 기능에 대한 코드를 찾으려고 조금 당황했습니다. 반면에-나는 다른 대답 ( getAnywhere다른 목적으로 사용 하고 있음) 의 흐름에 적합하지 않다고 생각했습니다 .
MichaelChirico

10

R에는 매우 편리한 기능이 있습니다 edit

new_optim <- edit(optim)

그것은 소스 코드를 열 것입니다 optimR에 지정된 편집기 사용하여options 다음 편집하고 수정 된 기능을에 할당 할 수 있습니다 new_optim. 이 함수는 코드를 보거나 코드를 디버깅하는 데 매우 유용 debug합니다. 예를 들어 일부 메시지 나 변수를 인쇄하거나 추가 조사를 위해 전역 변수에 할당 할 수도 있습니다 (물론 사용할 수 있습니다 ).

소스 코드 만보고 콘솔에 성가신 긴 소스 코드를 인쇄하지 않으려면

invisible(edit(optim))

분명히 이것은 C / C ++ 또는 Fortran 소스 코드를 보는 데 사용할 수 없습니다.

BTW edit는 목록, 행렬 등과 같은 다른 객체를 열 수 있으며 속성이있는 데이터 구조를 보여줍니다. 이 함수 de는 편집기와 같은 엑셀 (GUI가 지원하는 경우)을 열어 매트릭스 또는 데이터 프레임을 수정하고 새 프레임을 반환하는 데 사용할 수 있습니다. 이것은 때로는 편리하지만 일반적인 경우, 특히 행렬이 큰 경우에는 피해야합니다.


3
이 접근 방식은 함수를 인쇄하는 것과 동일한 함수 소스 만 나타냅니다 (즉, 질문에서와 동일). 그보다 더 심오한 것이이 질문에 관한 것입니다.
Brian Diggs

2
@BrianDiggs 네, 맞습니다. 여호수아가 아주 완전한 대답을했기 때문에 나는 그 질문에 답을하려고하지 않았습니다. 나는 단지 주제와 관련된 무언가를 추가하려고 시도하는데, 흥미롭고 알아두면 도움이 될 수 있습니다.
Eric

8

함수가 C / C ++ / Fortran이 아닌 순수한 R로 작성되는 한 다음을 사용할 수 있습니다. 그렇지 않으면 가장 좋은 방법은 디버깅 하고 " jump into "를 사용하는 것입니다.

> functionBody(functionName)

2
이것은와 동일합니다 body. identical(functionBody, body)입니다 TRUE.
Joshua Ulrich

1
base::body그리고 methods::functionBody비록 그들이 탈선 될 가능성은 낮습니다. body너무 오버라이드 (override) 할 수있다 : rdocumentation.org/search?q=body
Moody_Mudskipper

7

RStudio에는 3 가지 방법이 있습니다.

  1. 커서가 기능 중일 때 F2 키를 누르십시오.
  2. Ctrl 또는 Command를 누른 상태에서 함수 이름을 클릭하십시오.
  3. View(function_name) (위에서 언급 한 바와 같이)

소스 코드와 함께 새 창이 열립니다. .Primitive 또는 .C에 도달하면 다른 방법이 필요합니다. 죄송합니다.


5

View([function_name])-예. View(mean)대문자 [V]를 사용해야합니다. 읽기 전용 코드가 편집기에서 열립니다.


5

print.function()S3 제네릭 인을 사용하여 콘솔에서 함수 쓰기를 시도 할 수도 있습니다 .


3
print.function()S3 방법 입니다. 일반은 print()입니다. 그리고 일반적으로 메소드를 직접 호출하는 것은 좋지 않습니다. 그것은 일반적인 함수와 메소드 디스패치의 전체 목적을 상실합니다.
Joshua Ulrich
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.