Finding out which functions are called within a given function

dependencies, function, function-declaration, r, recursion

Solution

There must be better ways out there, but here's my attempt:

listFunctions <- function(function.name, recursive = FALSE, 
                          checked.functions = NULL){

    # Get the function's code:
    function.code <- deparse(get(function.name))

    # break code up into sections preceding left brackets:
    left.brackets <- c(unlist(strsplit(function.code, 
                                       split="[[:space:]]*\\(")))

    called.functions <- unique(c(unlist(sapply(left.brackets, 
                                               function (x) {

        # Split up according to anything that can't be in a function name.
        # split = not alphanumeric, not '_', and not '.'
        words <- c(unlist(strsplit(x, split="[^[:alnum:]_.]")))

        last.word <- tail(words, 1)
        last.word.is.function <- tryCatch(is.function(get(last.word)),
                                      error=function(e) return(FALSE))
        return(last.word[last.word.is.function])
    }))))

    if (recursive){

        # checked.functions: We need to keep track of which functions 
        # we've checked to avoid infinite loops.
        functs.to.check <- called.functions[!(called.functions %in%
                                          checked.functions)]

        called.functions <- unique(c(called.functions,
            do.call(c, lapply(functs.to.check, function(x) {
                listFunctions(x, recursive = T,
                              checked.functions = c(checked.functions,          
                                                    called.functions))
                }))))
    }
    return(called.functions)
}

And the results:

> listFunctions("listFunctions", recursive = FALSE)
 [1] "function"      "deparse"       "get"           "c"            
 [5] "unlist"        "strsplit"      "unique"        "sapply"       
 [9] "tail"          "tryCatch"      "is.function"   "return"       
[13] "if"            "do.call"       "lapply"        "listFunctions"

> system.time(all.functions <- listFunctions("listFunctions", recursive = TRUE))
   user  system elapsed 
  92.31    0.08   93.49 

> length(all.functions)
  [1] 518

As you can see, the recursive version returns a lot of functions. The problem with this is it returns every function called in the process, which obviously adds up as you go. In any case, I hope you can use this (or modify it) to suit your needs.

Problem

Possible Duplicate: Generating a Call Graph in R I'd like to systematically analyze a given function to find out which other functions are called within that very function. If possible, recursively. I came across this function in a blog post by milktrader with which I can do something similar for packages (or namespaces) ``` listFunctions <- function( name, ... ){ name.0 <- name name <- paste("package", ":", name, sep="") if (!name %in% search()) { stop(paste("Invalid namespace: '", name.0, "'")) } # KEEP AS REFERENCE # out <- ls(name) funlist <- lsf.str(name) out <- head(funlist, n=length(funlist)) return(out) } > listFunctions("stats") [1] "acf" "acf2AR" "add.scope" [4] "add1" "addmargins" "aggregate" [7] "aggregate.data.frame" "aggregate.default" "aggregate.ts" [10] "AIC" "alias" "anova" .... [499] "xtabs" ``` Yet, I'd like a function where `name` would be the name of a function and the return value is a character vector (or a list, if done recursively) of functions that are called within `name`. Motivation I actually need some sort of character based output (vector or list). The reason for this is that I'm working on a generic wrapper function for parallelizing an abitrary "inner function" where you don't have to go through a time consuming trial-and-error process in order to find out which other functions the inner function depends on. So the output of the function I'm after will directly be used in `snowfall::sfExport()` and/or `snowfall::sfSouce`. EDIT 2012-08-08 As there's been some close-votes due to duplicity, I'll check how answers can be merged with the other question tomorrow.

Original source

Related problems