查找R表达式中所有函数的名称

时间:2013-01-11 10:59:59

标签: r function metaprogramming expression

我正在尝试查找任意合法R表达式中使用的所有函数的名称,但是我找不到将下面的示例标记为函数而不是名称的函数。

test <- expression(
    this_is_a_function <- function(var1, var2){

    this_is_a_function(var1-1, var2)
})

all.vars(test, functions = FALSE)

[1] "this_is_a_function" "var1"              "var2" 

all.vars(expr,functions = FALSE)似乎在表达式中返回函数声明(f&lt; - function(){}),同时过滤掉函数调用('+'(1,2),... )。

核心库或其他地方是否有任何函数将'this_is_a_function'标记为函数,而不是名称?它需要处理任意表达式,这些表达式在语法上是合法的,但可能无法正确评估(例如'+'(1,'duck'))

我找到similar questions,但它们似乎没有包含解决方案。

如果需要澄清,请在下面留言。我正在使用解析器包来解析表达式。

编辑:@Hadley

我的表达式包含整个脚本,通常包含一个包含嵌套函数定义的main函数,并在脚本末尾调用main函数。

函数都是在表达式中定义的,我不介意我是否必须包含'&lt; - '和'{',因为我可以很容易地将它们自己过滤掉。

我的动机是获取所有R脚本,并收集有关我的函数使用方式随时间变化的基本统计信息。

编辑:当前解决方案

基于Regex的方法获取函数定义,并结合James的注释中的方法来获取函数调用。通常有效,因为我从不使用右手任务。

function_usage <- function(code_string){
    # takes a script, extracts function definitions

    require(stringr)

    code_string <- str_replace(code_string, 'expression\\(', '')

    equal_assign <- '.+[ \n]+<-[ \n]+function'
    arrow_assign <- '.+[ \n]+=[ \n]+function'

    function_names <- sapply(
        strsplit(
            str_match(code_string, equal_assign), split = '[ \n]+<-'),    
        function(x) x[1])

    function_names <- c(function_names, sapply(
        strsplit(
            str_match(code_string, arrow_assign), split = '[ \n]+='),    
            function(x) x[1]))

        return(table(function_names))    
    }

2 个答案:

答案 0 :(得分:4)

简答: is.function检查变量是否实际包含函数。这对于(未评估的)呼叫不起作用,因为它们是呼叫。你还需要照顾掩蔽:

mean <- mean (x)

更长的回答:

恕我直言,this_is_a_function的两次出现有很大的不同。

在第一种情况下,在评估表达式后,您将为名称为this_is_a_function 的变量分配一个函数。差异与2+24之间的差异相同 但是,仅查找<- function ()并不能保证结果是函数:

f <- function (x) {x + 1} (2)

第二次出现在语法上是一个函数调用。您可以从表达式中确定需要存在一个包含函数的名为this_is_a_function的变量,以便正确评估调用。但是:你不知道它是否只存在于该声明中。但是,您可以检查是否存在这样的变量,以及它是否是函数。

函数存储在变量中的事实也像其他类型的数据一样,这意味着在第一种情况下,您可以知道function ()的结果将是函数,并从中得出结论,紧接在此表达式之后在评估时,名为this_is_a_function的变量将保存一个函数。

然而,R充满了名称和功能:“ - &gt;”是赋值函数的名称(一个包含赋值函数的变量)......

评估表达式后,您可以is.function (this_is_a_function)验证这一点。 但是,这绝不是返回函数的唯一表达式:想想

f <- function () {g <- function (){}}
> body (f)[[2]][[3]]
function() {
}
> class (body (f)[[2]][[3]])
[1] "call"
> class (eval (body (f)[[2]][[3]]))
[1] "function"
  

all.vars(expr,functions = FALSE)似乎在表达式中返回函数声明(f&lt; - function(){}),同时过滤掉函数调用('+'(1,2),... )。

我会说它是相反的:在那个表达式f中是变量(name),它将被赋予函数(一旦评估了调用)。 +(1,2)计算为数字。除非你不这样做。

e <- expression (1 + 2)
> e <- expression (1 + 2)
> e [[1]]
1 + 2
> e [[1]][[1]]
`+`
> class (e [[1]][[1]])
[1] "name"
> eval (e [[1]][[1]])
function (e1, e2)  .Primitive("+")
> class (eval (e [[1]][[1]]))
[1] "function"

答案 1 :(得分:2)

而不是寻找功能定义,而在没有实际评估函数的情况下实际上无法正确执行,而是更容易查找函数调用。

以下函数递归地捕获表达式/调用树,返回像函数一样调用的所有对象的名称:

find_calls <- function(x) {
  # Base case
  if (!is.recursive(x)) return()

  recurse <- function(x) {
    sort(unique(as.character(unlist(lapply(x, find_calls)))))
  }

  if (is.call(x)) {
    f_name <- as.character(x[[1]])
    c(f_name, recurse(x[-1]))
  } else {
    recurse(x)
  }
}

对于一个简单的测试用例,它可以正常工作:

x <- expression({
  f(3, g())
  h <- function(x, y) {
    i()
    j()
    k(l())
  }
})
find_calls(x)
# [1] "{"        "<-"       "f"        "function" "g"        "i"        "j"  
# [8] "k"        "l"