Tüm kaynaklı işlevleri alın


11

R, source()bazı işlevleri yüklemek için kullanıyorum :

source("functions.R")

Bu dosyada tanımlanan tüm işlevlerin listesini almak mümkün müdür? İşlev adları olarak. (Belki source()de bir şekilde onu iade edebilir?).

Not: Son çare, source()ikinci kez çağırmak local({ source(); })ve daha sonra ls()içeride yapmak ve işlevleri filtrelemek olacaktır, ancak bu çok karmaşık - daha kolay ve daha az beceriksiz bir çözüm var mı?


1
Bu işe yaramaz source(), ancak bu eski konu ilginizi çekebilir.
Andrew

1
@Andrew teşekkürler, önerilen çözümleri kontrol ettim ama bu soruda sunduğum son çare daha çılgınca geliyor :)
TMS

2
Bu çözümün daha çılgın olduğunu bilmiyorum:envir <- new.env() source("functions.R", local=envir) lsf.str(envir)
LocoGris

2
Kaynak dosyalarınızdan bir paket oluşturun. Ardından bir paket ad alanı da dahil olmak üzere tüm avantajları elde edersiniz.
Roland

@TMS, sorunuzu yanlış anlamış / tanımlanmış işlevler istediğinizi okumamış . Özür!
Andrew

Yanıtlar:


7

Bence en iyi yol, dosyayı geçici bir ortama aktarmak olacaktır. Bu işlevi tüm işlevler için sorgulayın, ardından bu değerleri üst ortama kopyalayın.

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

teşekkürler, şu anda tek çözüm olarak bu çözüm umut verici görünüyor! Şaşırtıcı bir şekilde, en az oy veren olan. Bu son çare olarak bahsettiğim, ama new.env()zarif yerine yerine ana çerçeve local({ })ile çalışacak emin değilim kullanarak assign.
TMS

1) Sence bununla çalışır local()mı? Ve BTW, 2) for döngüsünde ne yaparsınız: ortamları birleştirmek için bir işlev yok mu?
TMS

1
@TMS Denemedim ama yerel ile çalışabilir. Tüm değişkenleri bir ortamdan diğerine kopyalamanın başka bir yolunun farkında değilim. Bu yaygın bir işlem değil.
MrFlick

Bence attachbir ortamı başka bir ortama bağlamak için kullanılabilir. Gerçi posargümanı belirtmek yerine kullanmak zorundasınız parent.frame. Ve sadece tüm ortamı kopyalamak için iyi çalışır, MrFlick'in fordöngüsü sadece işlevleri kopyalamanızı sağlar.
Gregor Thomas

5

Biraz hantal ama sourceçağrı öncesi ve sonrası gibi nesnelerdeki değişikliklere bakabilirsiniz .

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

Teşekkürler! Ben de bu fikri vardı ama çok basit bir nedenle işe yaramaz - paket zaten yüklenmişse (Bu kodu hata ayıklama zaman her zaman olur, ben sadece kaynakları yeniden kaynak), o zaman hiçbir şey döndürmez.
TMS

3

Bu normal ifadenin hemen hemen her geçerli işlev türünü (ikili işleç, atama işlevleri) ve işlev adındaki her geçerli karakteri yakaladığını düşünüyorum, ancak bir kenar durumu kaçırmış olabilirim.

# 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
fyi Bence bu gerçekten iyi bir çözüm değil ama kesinlikle eğlenceli bir çözüm. Bu bilgiye gerçekten ihtiyacım olursa dosyayı muhtemelen bir pakete dönüştürürüm.
alan ocallaghan

İki uç vakayı kaçırdım! Fonksiyonlar ile başlayabilir .ve fonksiyon `foo<-`<- function(x, value)
atayabilir

=Görev için kullanıyorum , bu işlevlerimi yakalamayacak ...
Gregor Thomas

İyi yakalama - düzenlendi. R'nin ` d d` <- function(x)şu anda yakalanmayan aptalca şeyler yapmanıza izin verdiğini not edeceğim . Regex'in çok saçma olmasını istemiyorum, ancak tekrar ziyaret edebilirim.
alan ocallaghan

Ayrıca, birlikte işlevleri atayabilir assign, <<-ve ->. Ve bu yaklaşımın işlevler içinde tanımlanan , ancak aslında kaynaklı ortamda olmayan işlevleri hesaba katması çok zor olacaktır . Cevabınız standart durumlar için çok iyi çalışmalıdır, ancak aslında regex dışında bir R ayrıştırıcısı yazmak istemezsiniz.
Gregor Thomas

1

Bu sizin kendi komut dosyanızsa, nasıl biçimlendirildiğini denetleyebilmeniz için basit bir kural yeterli olacaktır. Her işlev adının satırındaki ilk karakterden başladığından ve sözcüğün functionbu satırda da göründüğünden emin olun. Kelimenin diğer herhangi bir kullanımı functionboşluk veya sekme ile başlayan bir satırda görünmelidir. Sonra tek satırlık bir çözüm:

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

Bu yaklaşımın avantajları

  • çok basit . Kurallar basitçe ifade edilir ve işlev adlarını çıkarmak için sadece bir basit R kodu satırı vardır. Regex de basittir ve mevcut bir dosya için kontrol edilmesi çok kolaydır - sadece kelimeyi grep functionve görüntülenen her oluşumun kurala uyup uymadığını kontrol edin.

  • kaynağı çalıştırmaya gerek yok. Tamamen statiktir .

  • çoğu durumda kaynak dosyayı değiştirmenize gerek kalmayacak ve diğerlerinde asgari değişiklikler olacaktır. Senaryoyu bu düşünceyle sıfırdan yazıyorsanız, düzenlemek daha da kolaydır.

Sözleşmeler fikri boyunca birçok alternatif var. daha karmaşık bir ifadeye sahip olabilirsiniz veya # FUNCTIONkomut dosyasını sıfırdan yazıyorsanız ve daha sonra bu ifadeyi yazıp ilk kelimeyi satırda ayıklarsanız, herhangi bir işlev tanımının ilk satırının sonuna ekleyebilirsiniz , ancak burada ana öneri görünüyor basitliği ve listelenen diğer avantajları nedeniyle özellikle çekici.

Ölçek

# 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))Bunu kırmak istiyorum
alan ocallaghan

@alan ocallaghan, Örneğiniz, geçerli kuralların oluşmaması için belirtilen kuralları ihlal ediyor. Bunu yazmak ve yine de kurallar dahilinde kalmak için, girintili yeni bir satırda çalışmaya başlaması veya lapply'e girintili olması gerekebilir.
G. Grothendieck

Belirli bir biçimlendirme gerekiyorsa, yardımcı program büyük ölçüde bozulmuş olduğunu düşünüyorum, çünkü bu dosyayı değiştirmeyi gerektirebilir - bu durumda, kullanıcının işlev adlarını manuel olarak okumasını da önerebilirsiniz
alan ocallaghan

1
Bu, yalnızca dosyayı kontrol etmezseniz dikkate alınır, ancak bu olasılığı hariç tuttuk. Programlamada kuralların kullanımı çok yaygındır. Sık sık # TODObenim kod koymak koymak , örneğin yapmak benim grep olabilir. Aynı satırlar boyunca başka bir olasılık # FUNCTION, herhangi bir fonksiyon tanımının ilk satırının sonuna yazılabilir .
G. Grothendieck

1
regex ile ayrıştırma yapmaya çalışıyor cehenneme giden yol ....
TMS

0

Bu, bir dizi belirteç (sembol, atama operatörü, sonra işlev) aramak için yayında kullanılan kodu yorumumdan uyarlar ve tanımlanmış herhangi bir işlevi almalıdır. MrFlick'in cevabı olarak sağlam olup olmadığından emin değilim, ancak başka bir seçenek:

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"]]
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.