Déterminer le nom de la fonction dans cette fonction

15

Comment puis-je obtenir le nom de la fonction dans cette fonction non anonyme? ci-dessous, je suppose qu'il existe une fonction ou un processus pour ce faire appelé magical_r_function()et quelles seraient les sorties attendues.

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"
Tyler Rinker
la source

Réponses:

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

Démo:

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"
r2evans
la source
Tyler, ça ne me dérange certainement pas (et GG est bon aussi), mais quels étaient vos critères pour quelle réponse choisir?
r2evans
Bonne question. Les deux excellents choix. Les deux semblaient fonctionner de la même manière dans mes tests. GG a fourni un peu plus de détails. C'était difficile de décider.
Tyler Rinker
En examinant de plus près la dernière condition d'attribution d'une fonction à un nouveau nom, celui-ci s'aligne plus étroitement avec la demande d'origine.
Tyler Rinker
Veuillez ne pas changer uniquement sur mon commentaire! Je ne branle pas et n'ai pas besoin de représentants (bien que vous ayez tous un peu plus que moi). Non, j'étais simplement curieux. Je pense que les deux match.callet sys.callsont des fonctions de base valides avec peu de différence dans "l'effet" et "les exigences". J'étais donc curieux de savoir ce que vous pourriez avoir en préférant l'un à l'autre.
r2evans
12

Essayez sys.call(0)si la sortie d'un objet d'appel est correcte ou analysez-la si vous voulez juste que le nom soit une chaîne de caractères. Voici quelques tests à ce sujet. sys.call renvoie à la fois le nom et les arguments et [[1]] choisit uniquement le nom.

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

g <- function() my_fun()

my_fun()
## [1] "my_fun"

g()
## [1] "my_fun"

Noms des fonctions

Notez que les fonctions n'ont pas de noms. Ce que nous considérons comme des noms de fonction ne sont en fait que des variables qui détiennent la fonction et ne font pas partie de la fonction elle-même. Une fonction se compose d'arguments, de corps et d'un environnement - il n'y a pas de nom de fonction parmi ces constituants.

Fonctions anonymes

De plus, on peut avoir des fonctions anonymes et celles-ci peuvent retourner des résultats étranges lorsqu'elles sont utilisées avec ce qui précède.

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

Cas de bord

Il existe certaines situations, en particulier impliquant des fonctions anonymes, où deparseretournera plus d'un élément, donc si vous voulez couvrir de tels cas marginaux, utilisez l'argument nlines = 1 pour analyser ou utilisez deparse (...) [[1]] ou as mentionné par @Konrad Rudolph en utilisant deparse1 dans R 4.0.0.

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

Autre

Rappel . Si la raison pour laquelle vous voulez que le nom de la fonction soit d'appeler récursivement la fonction, utilisez Recall()plutôt. Depuis le fichier d'aide:

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

avertissement et arrêt Ces deux émettent le nom de la fonction ainsi que tout argument qui leur est transmis, il n'est donc pas nécessaire d'obtenir le nom de la fonction actuelle.

testWarning <- function() warning("X")
testWarning()
## Warning message:
## In testWarning() : X
G. Grothendieck
la source
2
Votre «cas de bord» est résolu avec élégance dans R 4.0 via l'introduction de la deparse1fonction. Je suppose que nous devrions commencer à utiliser cela au lieu de deparsepar défaut, une fois que l'adoption est suffisamment élevée.
Konrad Rudolph
+1 pour Recall, ce qui me semble être ce dont OP avait vraiment besoin. Cependant, votre exemple de la séquence de Fibonacci n'est pas vraiment bon: il a le problème que vous répétiez souvent les appels: car fib(10), fib(8)est appelé 2 fois au total (une fois fib(10)directement, une fois par fib(9)), fib(7)est appelé 3 fois, fib(6)est appelé 5 fois. Vous voyez où cela va?
Emil Bode
@Emil, Ceci est directement de la page d'aide Recall (comme indiqué dans la réponse), donc il illustre certainement le point. Si vous ne l'aimez pas pour d'autres raisons, vous pouvez vous plaindre auprès des développeurs R.
G. Grothendieck
5

Nous pouvons également utiliser

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

my_fun()
#[1] "my_fun"
akrun
la source