모든 소스 함수 가져 오기


11

R에서는 source()일부 함수를로드 하는 데 사용하고 있습니다.

source("functions.R")

이 파일에 정의 된 모든 함수 목록을 얻을 수 있습니까? 함수 이름으로. (어쩌면 source()스스로가 그것을 반환 할 수 있습니까?).

추신 : 마지막 수단은 source()두 번째 로 전화를 local({ source(); })한 다음 ls()내부 및 필터 기능 을 수행하는 것이지만 너무 복잡합니다. 더 쉽고 덜 어설픈 해결책이 있습니까?


1
이것은을 사용하지 source()않지만이 오래된 스레드 가 관심이있을 수 있습니다.
앤드류

1
@Andrew 덕분에, 제안 된 솔루션을 확인했지만 문제에서 제시 한 마지막 방법보다 더 미치게 들립니다. :
TMS

2
:이 솔루션은 미쳤입니다 그것을 알고하지는envir <- new.env() source("functions.R", local=envir) lsf.str(envir)
LocoGris

2
소스 파일에서 패키지를 만드십시오. 그런 다음 패키지 네임 스페이스를 포함한 모든 이점을 얻을 수 있습니다.
Roland

@ TMS, 귀하의 질문을 오해 / 정의 된 기능 을 원한다는 것을 읽지 못했습니다 . 사과!
앤드류

답변:


7

가장 좋은 방법은 파일을 임시 환경으로 소싱하는 것입니다. 모든 기능에 대해 해당 환경을 조회 한 후 해당 값을 상위 환경에 복사하십시오.

my_source <- function(..., local=NULL) {
  tmp <- new.env(parent=parent.frame())
  source(..., local = tmp)
  funs <- names(tmp)[unlist(eapply(tmp, is.function))]
  for(x in names(tmp)) {
    assign(x, tmp[[x]], envir = parent.frame())
  }
  list(functions=funs)
}

my_source("script.R")

감사합니다.이 솔루션은 현재 유일하게 유망 해 보입니다! 놀랍게도, 공감 율이 가장 적은 사람. 내가 마지막 수단으로 언급 한 것이지만 new.env()우아함 대신에 사용 하여 부모 프레임에서 local({ })작동하는지 확실하지 않습니다 assign.
TMS

1) 당신은 그것이 작동한다고 생각 local()합니까? 그리고 BTW, 2) for 루프에서 수행하는 작업 : 환경을 병합하는 기능이 있습니까?
TMS

1
@TMS 로컬로 작동하지만 시도하지는 않았습니다. 한 환경에서 다른 환경으로 모든 변수를 복사하는 다른 방법을 알지 못합니다. 일반적인 작업이 아닙니다.
MrFlick

attach한 환경을 다른 환경에 연결하는 데 사용할 수 있다고 생각 합니다. 을 pos지정하는 대신 인수 를 사용해야 합니다 parent.frame. 또한 전체 환경을 복사하는 데만 효과적이며 MrFlick의 for루프를 사용하면 기능 만 복사 할 수 있습니다.
Gregor Thomas

5

약간 어색하지만 source호출 전후에 객체의 변경 사항을 볼 수 있습니다.

    # optionally delete all variables
    #rm(list=ls())

    before <- ls()
    cat("f1 <- function(){}\nf2 <- function(){}\n", file = 'define_function.R')
    # defines these
    #f1 <- function() {}
    #f2 <- function() {}
    source('define_function.R')
    after <- ls()

    changed <- setdiff(after, before)
    changed_objects <- mget(changed, inherits = T)
    changed_function <- do.call(rbind, lapply(changed_objects, is.function))
    new_functions <- changed[changed_function]

    new_functions
    # [1] "f1" "f2"

감사! 나는이 아이디어도 가지고 있었지만 매우 간단한 이유로 작동하지 않습니다. 패키지가 이미로드 된 경우 (코드를 디버깅 할 때 항상 발생하며 소스를 다시 소싱합니다) 아무 것도 반환하지 않습니다.
TMS

3

이 정규 표현식은 거의 모든 유효한 유형의 함수 (이진 연산자, 할당 함수)와 함수 이름의 모든 유효한 문자를 포착한다고 생각하지만 가장자리가 빠졌을 수 있습니다.

# lines <- readLines("functions.R")

lines <- c(
  "`%in%` <- function",
  "foo <- function",
  "foo2bar <- function",
  "`%in%`<-function",
  "foo<-function",
  ".foo <-function",
  "foo2bar<-function",
  "`foo2bar<-`<-function",
  "`foo3bar<-`=function",
  "`foo4bar<-` = function",
  "` d d` <- function", 
  "lapply(x, function)"
)
grep("^`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?\\s*(<-|=)\\s*function", lines)
#>  [1]  1  2  3  4  5  6  7  8  9 10
funs <- grep("^`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?\\s*(<-|=)\\s*function", lines, value = TRUE)
gsub("^(`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?).*", "\\1", funs)
#>  [1] "`%in%`"      "foo "        "foo2bar "    "`%in%`"      "foo"        
#>  [6] ".foo "       "foo2bar"     "`foo2bar<-`" "`foo3bar<-`" "`foo4bar<-`"

1
참고로 이것이 실제로는 좋은 해결책은 아니지만 확실히 재미있는 해결책 이라고 생각합니다 . 이 정보가 정말로 필요한 경우 파일을 패키지로 변환 할 것입니다.
alan ocallaghan

나는 두 가지 경우를 놓쳤다! 기능을 시작할 수 있습니다 .(및 할당 기능 `foo<-`<- function(x, value)이 존재합니다.
ocallaghan 앨런

나는 =과제를 위해 사용하는데 , 이것은 내 기능 중 어느 것도 포착하지 못할 것입니다.
Gregor Thomas

좋은 캐치-편집. 나는 R ` d d` <- function(x)이 현재 잡히지 않은 것과 같은 바보 같은 일을 할 수있게 해줍니다 . 나는 정규 표현식이 너무 어리석은 것을 원하지 않지만 재 방문 할 수도 있습니다.
alan ocallaghan

또한,과 기능을 할당 할 수 있습니다 assign, <<-그리고 ->. 그리고이 접근법은 함수 내에 정의 되었지만 실제로는 소스 환경에는없는 함수를 설명하기가 매우 어려울 것입니다. 당신의 대답은 표준 경우에는 잘 작동하지만 실제로 정규식으로 R 파서를 작성하고 싶지는 않습니다.
Gregor Thomas

1

이것이 자신의 스크립트이므로 형식화 방법을 제어 할 수 있으면 간단한 규칙으로 충분합니다. 각 함수 이름이 해당 행의 첫 번째 문자에서 시작하고 해당 행 function에도 단어 가 나타나는지 확인하십시오 . 다른 단어 사용은 function공백이나 탭으로 시작하는 줄에 나타나야합니다. 그런 다음 한 줄 솔루션은 다음과 같습니다.

sub(" .*", "", grep("^\\S.*function", readLines("myscript.R"), value = TRUE))

이 방법의 장점은

  • 매우 간단 합니다. 규칙은 간단하게 설명되어 있으며 함수 이름을 추출하는 데 필요한 간단한 R 코드 행은 하나뿐입니다. 정규 표현식도 간단하고 기존 파일의 경우 확인하기가 매우 쉽습니다. 단어를 grep하고 function표시된 각 항목이 규칙을 따르는 지 확인하십시오.

  • 소스를 실행할 필요가 없습니다. 완전히 정적 입니다.

  • 대부분의 경우 소스 파일 을 전혀 변경할 필요가 없으며 다른 경우에는 최소한의 변경이 있습니다. 이 점을 염두에두고 스크립트를 처음부터 작성하는 경우 훨씬 쉽게 정리할 수 있습니다.

컨벤션 아이디어에는 다른 많은 대안이 있습니다. # FUNCTION스크립트를 처음부터 새로 작성하고 해당 문구를 grep하고 줄에서 첫 번째 단어를 추출하는 경우 더 정교한 정규 표현식을 사용하거나 함수 정의의 첫 번째 줄 끝에 추가 할 수 있지만 주요 제안은 다음과 같습니다. 단순성과 나열된 다른 장점으로 인해 특히 매력적입니다.

테스트

# generate test file
cat("f <- function(x) x\nf(23)\n", file = "myscript.R") 

sub(" .*", "", grep("^\\S.*function", readLines("myscript.R"), value = TRUE))
## [1] "f"

lapply(x, function(y) dostuff(y))이 깰 것
ocallaghan 앨런을

@alan ocallaghan, 귀하의 예는 명시된 규칙을 위반하여 유효하게 발생할 수 없습니다. 이것을 작성하고 여전히 규칙 내에서 유지하려면 들여 쓰기 된 새 줄에서 기능을 시작해야하거나 랩을 들여 쓰기해야 할 수도 있습니다.
G. Grothendieck

이 경우, 당신은뿐만 아니라 사용자가 함수 이름을 수동으로 읽어 제안 할 수 있습니다 - 당신이 특정 형식을 요구하는 경우 그 파일 변경이 필요할 수 있기 때문에 유틸리티가 대규모 저하 생각
ocallaghan 앨런을

1
파일을 제어하지 않지만 그 가능성을 배제한 경우에만 고려해야합니다. 규칙 사용은 프로그래밍에서 매우 일반적입니다. # TODO예를 들어, 종종 내 코드 전체에 넣어서 할 일을 grep 할 수 있습니다. 같은 줄을 따라 또 다른 가능성 # FUNCTION은 함수 정의의 첫 줄 끝에 쓸 수 있습니다 .
G. Grothendieck

1
정규 표현식으로 파싱을 시도하는 것은 지옥으로가는 길입니다.
TMS

0

이것은 내 의견의 게시물에 사용 된 코드를 일련의 토큰 (기호, 할당 연산자, 함수)을 검색하도록 조정하며 정의 된 함수를 가져 와야합니다. MrFlick의 답변으로 강력한 지 확실하지 않지만 다른 옵션입니다.

source2 <- function(file, ...) {
  source(file, ...)
  t_t <- subset(getParseData(parse(file)), terminal == TRUE)
  subset(t_t, token == "SYMBOL" & 
           grepl("ASSIGN", c(tail(token, -1), NA), fixed = TRUE) & 
           c(tail(token, -2), NA, NA) == "FUNCTION")[["text"]]
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.