その関数内の関数名を判別する


15

その非匿名関数内で関数の名前を取得するにはどうすればよいですか?以下では、これを実行するための関数またはプロセスが呼び出されmagical_r_function()、予想される出力がどうなるかを想定しています。

my_fun <- function(){
      magical_r_function()
}
my_fun()
## [1] "my_fun"


foo_bar <- function(){
      magical_r_function()
}
foo_bar()
## [1] "foo_bar"

ballyhoo <- function(){
    foo_bar()
}
ballyhoo()
## [1] "foo_bar"

tom_foolery <- foo_bar
tom_foolery()
## [1] "tom_foolery"

回答:


18
as.character(match.call()[[1]])

デモ:

my_fun <- function(){
  as.character(match.call()[[1]])
}
my_fun()
# [1] "my_fun"
foo_bar <- function(){
  as.character(match.call()[[1]])
}
foo_bar()
# [1] "foo_bar"
ballyhoo <- function(){
  foo_bar()
}
ballyhoo()
# [1] "foo_bar"
tom_foolery <- foo_bar
tom_foolery()
# [1] "tom_foolery"

タイラー、私は確かに気にしません(GGもいいです)が、どの回答を選択するための基準は何でしたか?
r2evans

良い質問。どちらも優れた選択肢です。私のテストではどちらも同じように機能するように見えました。GGはもう少し詳細を提供しました。決めるのは大変でした。
タイラーリンカー

関数を新しい名前に割り当てる最後の条件を詳しく調べると、これは元の要求とより密接に一致します。
タイラーリンカー

コメントだけを変更しないでください!私はおしゃべりではなく、担当者も必要ではありません(あなたが私よりも少し多いですが)。いいえ、興味がありました。私は両方を考えるmatch.callsys.call「効果」と「要件」にほとんど差と有効な基本機能です。だから私はあなたがどちらか一方をもう一方よりも好むかもしれない洞察に興味がありました。
r2evans

12

試してみてくださいsys.call(0)コールオブジェクトの出力は、あなただけの文字列として名をしたい場合は、[OK]というか、逆パースある場合。以下は、これに関するいくつかのテストです。sys.callは名前と引数の両方を返し、[[1]]は名前だけを取り出します。

my_fun <- function() deparse(sys.call(0)[[1]])

g <- function() my_fun()

my_fun()
## [1] "my_fun"

g()
## [1] "my_fun"

関数名

関数には実際には名前がないことに注意してください。関数名と見なすのは、実際には関数を保持する単なる変数であり、関数自体の一部ではありません。関数は、引数、本体、および環境で構成されます。これらの構成要素には関数名はありません。

匿名関数

さらに、匿名関数を使用することができ、これらを上記と一緒に使用すると奇妙な結果を返す可能性があります。

sapply(1:3, function(x) deparse(sys.call(0)[[1]]))
## [1] "FUN" "FUN" "FUN"

エッジケース

特に匿名関数を含むいくつかの状況が存在します。そこでdeparseは複数の要素が返されるため、このようなエッジのケースをカバーする場合は、nlines = 1引数を使用して、解析解除するか、deparse(...)[[1]]または@Konrad RudolphがR 4.0.0でdeparse1を使用して言及しました。

Map(function(x) deparse(sys.call(0)[[1]], nlines = 1), 1:2)
## [[1]]
## [1] "function (x) "
## 
## [[2]]
## [1] "function (x) "

Map(function(x) deparse(sys.call(0)[[1]]), 1:2)  # without nlines=1
## [[1]]
## [1] "function (x) "             "deparse(sys.call(0)[[1]])"
##
## [[2]]
## [1] "function (x) "             "deparse(sys.call(0)[[1]])"

その他の

リコール。関数名が必要な理由が関数を再帰的に呼び出すことである場合は、Recall()代わりに使用してください。ヘルプファイルから:

fib <- function(n)
   if(n<=2) { if(n>=0) 1 else 0 } else Recall(n-1) + Recall(n-2)
fib(4)
## [1] 3

警告と停止これらはどちらも、渡された引数とともに関数の名前を発行するため、現在の関数名を取得する必要はありません。

testWarning <- function() warning("X")
testWarning()
## Warning message:
## In testWarning() : X

2
deparse1関数の導入により、R 4.0では「エッジケース」がエレガントに解決されます。deparse採用が十分に高まったら、デフォルトではなく、それを使い始める必要があると思います。
Konrad Rudolph

+1 for Recall、これはOPが本当に必要としていたものだと思います。しかし、フィボナッチ数列のあなたの例では、本当に良いものではありません:それはあなたが頻繁に通話を繰り返してしまうという問題がありますためfib(10)fib(8)(一度、合計で2回呼び出されるfib(10)ことにより、一度、直接fib(9))、fib(7)、3回呼ばれるfib(6)と呼ばれています5回。これがどこに向かっているのかわかりますか?
Emil Bode

@Emil、これは(回答で述べたように)Recallのヘルプページの右側にあるため、要点を明確に示しています。他の理由で気に入らない場合は、R開発者に不満を言うことができます。
G.グロタンディーク

5

私たちも使用できます

my_fun <- function(){
  as.character(as.list(sys.calls()[[1]])[[1]])
 }

my_fun()
#[1] "my_fun"
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.