66

Say I have a bunch of functions, each with something likeMyFunction.1, etc. I want to pass these functions into another function, which prints out a small report. Ideally I'd like to be able to label sections of a report by which function is being used to generate the results.

So are there any nice ways of getting the name of a predefined function as a string?

Terru_theTerror
  • 4,918
  • 2
  • 20
  • 39
HamiltonUlmer
  • 2,389
  • 4
  • 19
  • 14

9 Answers9

68

I was wanting the same thing, and remembered library(foo) didn't need quotes, this is what it does:

package <- as.character(substitute(package))
nfultz
  • 681
  • 5
  • 2
26

Another approach would be to pass the names of the functions into your report function, and then get the functions themselves with the get() command. For instance:

function.names <- c("which","all")
fun1 <- get(function.names[1])
fun2 <- get(function.names[2])

Then you have the names in your original character vector, and the functions have new names as you defined them. In this case, the all function is now being called as fun2:

> fun2(c(TRUE, FALSE))
[1] FALSE

Or, if you really want to keep the original function names, just assign them locally with the assign function:

assign(function.names[2], get(function.names[2]))

If you run this command right now, you will end up with the all function in your ".GlobalEnv". You can see this with ls().

Shane
  • 98,550
  • 35
  • 224
  • 217
9

That may lead to parse(eval(...)) at which point you are open for this critique:

R> library(fortunes)
R> fortune("parse")

If the answer is parse() you should usually rethink the question.
   -- Thomas Lumley
      R-help (February 2005)

R>

So do your functions have to be called MyFunction.1 etc pp?

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • Hah - I was afraid I'd get that fortune. I just wanted to be able to have a function I can call like compare.distribution.methods(c(two.choice, go.left, simple.random.sample), and get a table of results for each method in the vector. The nice-to-have would be not having to pass in a labels vector with the names I want, but that's certainly another option. I was just hoping for something that'd do it for me. If this is not part of the R flow, then all the better that this question is answered thus. – HamiltonUlmer Oct 14 '09 at 17:26
  • 1
    The S3 method dispatch does that for you! Don't fear the "OO", this would be a nice simple example to start using it. If only I had some kind and gentle docs to point you to ... Maybe Chambers "Software for Data Analysis" book from last year? – Dirk Eddelbuettel Oct 14 '09 at 17:49
  • I am pretty certain the MAIN CORE of the `R hyperdrive` runs on `eval` and `parse` and `substitute` and `deparse` ... I guess those tools are only for the grownups. I don't have the VISOR like La Forge on Star Trek, so I guess I am not allowed to operate the hyperdrive. – mshaffer Sep 02 '22 at 12:07
6

You can get the unevaluated arguments of a function via match.call. For example:

> x <- function(y) print(match.call()[2])
> x(lm)
lm()
Jonathan Chang
  • 24,567
  • 5
  • 34
  • 33
  • 1
    True, not but helpful here. If you say "z <- lm" and then x(z) you get "z", not "lm". – Harlan Oct 14 '09 at 18:43
  • True, although it's unclear what the right behavior is then. Which function is "canonical" and should be printed? – Jonathan Chang Oct 15 '09 at 00:15
  • In a functional language, the name of the function is not really important. You can easily rename functions, or have functions without names. The match.call function just gives you the name of the variable that held the function when it was being called, so in that sense, it's doing the only thing it can do. I don't know that "canonical" really comes into it... – Harlan Oct 15 '09 at 02:13
  • This is all true, but then what is the right answer in the context of the original question. I imagine Hamilton wants a function `foo` that generates reports of the form > foo(MyFunction.1, MyFunction.2) MyFunction.1 867 MyFunction.2 5309 The question is if Hamilton sets `MyFunction.1 <- MyFunction.3`, should the report print out `MyFunction.1` or `MyFunction.3`? – Jonathan Chang Oct 15 '09 at 14:04
  • I should also mention that many functions, such as `lm`, use `match.call` to label its summary reports. – Jonathan Chang Oct 15 '09 at 14:10
  • MyFunction.1. Anything else requires telepathy on the part of the interpreter! The original name of the function is lost, per the S language definition, when you assign it to another variable. If you want the *original* name of the function, you have to store it in one of the several ways described above. – Harlan Oct 15 '09 at 18:32
  • So in light of this, what was your objection with my solution? – Jonathan Chang Oct 16 '09 at 00:02
4

Just want to provide an example to show the advantage and limitation in this issue:

I want to "save" a function with its name, as an option that would be used in another function:

R> foreach(..., .combine=test_fun) {...}

The test_fun is the name of the function, and of course

R> mode(test_fun)  
[1] "function"

When I use it in foreach, I just need the function name, while test_fun can be an existing function (e.g. cbind). So, test_fun is assigned by

R> test_fun <- get('cbind')

or

R> test_fun <- assign('cbind', get('cbind'))

So, you got the function here

R> test_fun  
function (..., deparse.level = 1)   
.Internal(cbind(deparse.level, ...))  

actually, the original name can't be maintained, so you have no way to convert test_fun back to string "cbind".

R> deparse(substitute(test_fun))  
[1] "test_fun"

I unfortunately need to deparse the foreach code so want the original name shown in the string. This means that the only way is to save 'cbind' as a string and creating such a function object brings no benefit in this case.

Marek
  • 49,472
  • 15
  • 99
  • 121
lionelc
  • 51
  • 2
3

When a function is passed around as an object, it loses its name. See, for example, the results of the following lines:

str(lm)
lm

You can get the arguments and the body of the function, but not the name.

My suggestion would be to construct a named list of functions, where the name could be printed:

> somefns <- list(lm=lm, aggregate=aggregate)
> str(somefns)
List of 2
 $ lm       :function (formula, data, subset, weights, na.action, method = "qr", 
    model = TRUE, x = FALSE, y = FALSE, qr = TRUE, singular.ok = TRUE, 
    contrasts = NULL, offset, ...)  
 $ aggregate:function (x, ...) 

> somefns[[1]](dist ~ speed, data=cars)

Call:
somefns[[1]](formula = dist ~ speed, data = cars)

Coefficients:
(Intercept)        speed  
     -17.58         3.93  

> names(somefns)[[1]]
[1] "lm"
Harlan
  • 18,883
  • 8
  • 47
  • 56
  • Two nice alternatives [have just been posted here](http://stackoverflow.com/questions/25621108/keeping-function-names-when-stored-in-an-object) – Boris Leroy Sep 02 '14 at 11:54
3

what about this:

deparse(quote(foo.bar))
mariotomo
  • 9,438
  • 8
  • 47
  • 66
3

Nice observation by @nfultz, so the answer to this thread's question would be :-

workingFunction <- Function(f)
{ functionName <- as.character(substitute(f)) }

Or

workingFunction <- Function(f)
{ functionName <- deparse(substitute(f)) }

All the other answers would simply return the parameter name itself ('f' in the example above) - I tried them all since I've been working on a function and experienced this issue of not being able to retrieve a function's name inside another function wherein the first is passed as parameter to the second function while calling it. Hope my answer helps to all those who might be stuck for the same!

0

I also struggled a lot with this issue and unfortunately the given solutions were not working out for me, but I came up with a way to get the name with R6 classes.

Here a minimal example:

NameProvider <-
  R6::R6Class(
    "NameProvider",
    base::list(
      get_fun_name = function(fun_name) { return(fun_name)})
    )

NameUser <-
  R6::R6Class(
    "NameUser",
    base::list(
      do_something = function(fun)
        {
          np <- NameProvider$new()
          fname <- np$get_fun_name(fun_name = base::as.character(base::substitute(fun)))
          do_the_thing <- paste0("THIS IS ", base::toupper(fname), "!!!")
          return(do_the_thing)
        }
        )
      )

nu <- NameUser$new()
nu$do_something(fun = mean) 

The class NameProvider just returns the name as a string and in the class NameUser the call for base::as.character(base::substitute(...)) is made.

Domingo
  • 613
  • 1
  • 5
  • 15