すべてのソース関数を取得する


11

Rでは、source()いくつかの関数をロードするために使用しています。

source("functions.R")

このファイルで定義されているすべての関数のリストを取得することは可能ですか?関数名として。(多分source()それ自体が何らかの形でそれを返すことができますか?)。

PSは:最後に呼び出すことであろうsource()ような二時間をlocal({ source(); })、次にんls()内側とフィルタ機能が、それはあまりにも複雑だ-簡単かつ少ない不器用な解決策はありますか?


1
これはを使用しませんsource()が、この古いスレッドは興味があるかもしれません。
アンドリュー、

1
@Andrewありがとう、私は提案された解決策をチェックしましたが、それは私が質問で提示した最後の手段よりもはるかに奇妙に聞こえます:)
TMS

2
私はこの解決策がもっと奇妙であることを知りません:envir <- new.env() source("functions.R", local=envir) lsf.str(envir)
LocoGris '15

2
ソースファイルからパッケージを作成します。次に、パッケージの名前空間を含むすべての利点が得られます。
Roland、

@TMS、あなたの質問を誤解した/あなたが定義された関数を望んでいることを読みませんでした。お詫び!
Andrew

回答:


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()か?ところで、2)forループで何をしているのですか?環境をマージする関数はありませんか?
TMS

1
@TMS試したことはありませんが、ローカルで動作する可能性があります。ある環境から別の環境にすべての変数をコピーする別の方法を知りません。これは一般的な操作ではありません。
MrFlick

attachある環境を別の環境に接続するために使用できると思います。ただしpos、を指定するのではなく、引数を使用する必要がありますparent.frame。また、環境全体forをコピーする場合にのみ機能します。MrFlickのループでは、関数のみをコピーできます。
グレゴールトーマス

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

この正規表現は、ほぼすべての有効なタイプの関数(2項演算子、代入関数)と関数名のすべての有効な文字をキャッチすると思いますが、大文字と小文字を区別していない可能性があります。

# 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
これは本当に良い解決策ではないと思いますが、間違いなく楽しい解決策です。この情報が本当に必要な場合は、おそらくファイルをパッケージに変換します。
アランocallaghan

2つのエッジケースを見逃しました!機能はして開始することができます.(と割り当て機能`foo<-`<- function(x, value)が存在する。
ocallaghanアラン

=は割り当てに使用しますが、これは私の機能のいずれもキャッチしません...
グレゴールトーマス

良いキャッチ-編集。Rを使用する` d d` <- function(x)と、現在は検出されないような愚かなことができることに注意します。私は再訪するかもしれませんが、私は正規表現が愚かになりすぎたくありません。
アランocallaghan

また、あなたが持つ機能を割り当てることができassign<<-->。また、このアプローチで関数内で定義されているが、実際にはソース環境にはない関数を説明するのは非常に困難です。あなたの答えは標準的なケースでは非常にうまく機能するはずですが、実際には正規表現からRパーサーを作成する必要はありません。
グレゴールトーマス

1

これが独自のスクリプトであり、そのフォーマット方法を制御できるようにする場合は、単純な規則で十分です。各関数名がその行の最初の文字から始まり、単語がその行にfunctionも表示されることを確認してください。その他の単語の使用は、functionスペースまたはタブで始まる行に表示されます。次に、1行のソリューションは次のとおりです。

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

このアプローチの利点は、

  • とても簡単です。ルールは簡単に説明されており、関数名を抽出するために必要なRコードは1行だけです。Regexも単純で、既存のファイルの場合は非常に簡単にチェックできます。単語functionをgrepし、表示された各オカレンスがルールに従っているかどうかをチェックするだけです。

  • ソースを実行する必要はありません。それは完全に静的です。

  • 多くの場合、ソースファイルをまったく変更する必要はなく、他の場合は最小限の変更があります。これを念頭に置いてスクリプトを最初から作成する場合は、配置がさらに簡単になります。

慣習のアイデアに沿って他の多くの選択肢があります。あなたはより洗練された正規表現を持つことができるか、またはあなたは追加することができます# 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.グロタンディーク

特定のフォーマットが必要な場合、ユーティリティは大幅に低下すると思います。ファイルを変更する必要がある可能性があるためです。その場合、ユーザーが関数名を手動で読むことをお勧めします
alan ocallaghan

1
これは、ファイルを制御しない場合の考慮事項ですが、その可能性は除外されています。規約の使用は、プログラミングでは非常に一般的です。私はしばしば# TODOコード全体に入れて、たとえば、私がやるべきことをgrepできるようにします。同じ行に沿った別の可能性# FUNCTIONは、関数定義の最初の行の終わりに書き込むことです。
G.グロタンディーク

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.