1

I have a similar question as this one, but a more special case.

Consider the following example code:

fun1 <- mean
fun2 <- max
fun3 <- median

Now I want to get the names of the functions assigned to the variables as charachters.

While I understand that this is not possible in general, the above case seems somewhat special:

l <- list(fun1 = fun1, fun2 = fun2, fun3 = fun3)
l
$fun1
function (x, ...)
UseMethod("mean")
<bytecode: 0x2793818>
<environment: namespace:base>
$fun2
function (..., na.rm = FALSE)  .Primitive("max")
$fun3
function (x, na.rm = FALSE)
UseMethod("median")
<bytecode: 0x28382c8>
<environment: namespace:stats>

So the output of print(funX) contains the name of the function assigned to funX.

How can I extract this information into a character vector?

mschilli
  • 1,884
  • 1
  • 26
  • 56

3 Answers3

1

Use findGeneric for the S3 generics:

fun1 <- mean

utils:::findGeneric("fun1", parent.frame())
#[1] "mean"

For a primitive function you can deparse the function body:

fun2 <- max
is.primitive(fun2)
#[1] TRUE

body <- deparse(fun2)
m <- gregexpr('(?<=\\.Primitive\\(\\").*(?=\\")', body, perl = TRUE)
regmatches(body, m)[[1]]
#[1] "max"
Roland
  • 127,288
  • 10
  • 191
  • 288
  • Thx a lot for your good suggestions. Your 2nd example help me to simplify [my solution](http://stackoverflow.com/a/35885909/2451238). Still, i was not able to wrap your approach into a function (using `findGeneric` if `is.primitive` and `deparse` otherwise, since `funX` will be copied again (to `fun` in my example) when passed as an argument to the higher-order function. In this case `utils:::findGeneric(as.character(quote(fun)), parent.frame())` returns `""`, maybe this can be done by replacing `parent.frame()` by another `envir`? – mschilli Mar 10 '16 at 07:43
  • `sapply(l,function(fun)utils:::findGeneric(as.character(quote(fun)),.GlobalEnv))` returns `"" "" ""` as did `sapply(l,function(fun)utils:::findGeneric(as.character(quote(fun)),parent.frame()))` before... – mschilli Mar 10 '16 at 11:53
  • @mschilli Check out `sapply(l,function(fun) as.character(quote(fun)))`. Then, you could try `sapply(names(l), function(fun) utils:::findGeneric(fun, as.environment(l)))`. – Roland Mar 10 '16 at 15:57
  • The 2nd one (`as.environment(l)`) looks *very* promising. The only problem is that still the (higher order) function needs to know the names of the function references outside of the (higher order) function *and* their environment. What I need is something like in [my answer](http://stackoverflow.com/a/35885909/2451238) that gets (a copy of) the function reference and prints the name of the function initialy assigned to it. The `sapply` is just an example. `get_fun(fun1)` should return `"mean"` and `sapply(list(foo=fun2,fun3),get_fun)` should return `"max" "median"` (with names `"foo" ""`). – mschilli Mar 10 '16 at 16:32
  • Sorry, this gets to complicated for my taste. I really don't get why something like this would be necessary at all. Why would you put the primitives and generics in a list? Can't you just put their names in a list and then use `getFunction` when you need to call them? – Roland Mar 10 '16 at 18:15
  • I could and would but I need to modify a high-level function that takes a function pointer as argument to summarize data for a plot to use the name of the summarizing function in the plot title. If I would write such a function, I would do as you suggest. But as this function is already there and I don't even know who uses it in which script, I cannot change the argument list (except for maybe adding optional arguments to the end). – mschilli Mar 11 '16 at 07:04
0

The best I could come up with so far is parsing the print output:

get_fun <- function(fun){
             lines <- capture.output(print(fun))
             pat <- "UseMethod|Primitive"
             index <- grep(pat, lines)
             line <- lines[index]
             pat <- paste0(".*(", pat, ")")
             chunk <- sub(pat, "", line)
             words <- strsplit(chunk, "\"")[[1]]
             return(words[2])
           }
sapply(l, get_fun)
    fun1     fun2     fun3
  "mean"    "max" "median"

But there has to be a more direct way. After all somehow these names make it to the print output in the first place.


edit: Based on Roland's answer, I was able to simplify the above function definition to the following:

get_fun <- function(fun){
             fun <- deparse(fun)
             chunk <- tail(fun, 1)
             words <- strsplit(chunk, "\"")[[1]]
             return(words[2])
           }

Still I hope for a more direct/robust solution (as a higher-order function, maybe returning "fun" for cases were the real underlying function can not be determined/does not exist).

mschilli
  • 1,884
  • 1
  • 26
  • 56
0
for (myfun in c(max,mean,median))
  print(gsub('^.*"(.*)".*','\\1',tail(deparse(myfun),1)))
user2276686
  • 2,046
  • 1
  • 16
  • 8
  • AFAICT that's basically the same approach as [my (updated) answer](https://stackoverflow.com/a/35885909/2451238) (based on [Roland](https://stackoverflow.com/users/1412059/roland)'s [answer](https://stackoverflow.com/a/35896121/2451238)). – mschilli Sep 27 '21 at 16:29
  • While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. Thank you! – Roman Sep 28 '21 at 12:33