6

我正在尝试查找任意合法 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 <- function(){}),同时过滤掉函数调用 ('+'(1,2), ...)。

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

我发现了类似的问题,但它们似乎不包含解决方案。

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

编辑:@哈德利

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

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

这样做的动机是获取我所有的 R 脚本并收集关于我对函数的使用如何随时间变化的基本统计数据。

编辑:当前解决方案

基于正则表达式的方法获取函数定义,并结合 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))    
    }
4

2 回答 2

4

简短回答: is.function检查变量是否实际包含函数。这不适用于(未评估的)调用,因为它们是调用。您还需要注意掩蔽:

mean <- mean (x)

更长的答案:

恕我直言,这两种情况之间存在很大差异this_is_a_function

this_is_a_function 在第一种情况下,您将在计算表达式后将函数分配给具有名称的变量。2+2差异与和之间的差异相同4
然而,仅仅 find<- function ()并不能保证结果是一个函数:

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

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

事实上,函数也像其他类型的数据一样存储在变量中,这意味着在第一种情况下,您可以知道function ()将是函数的结果,并由此得出结论,在计算此表达式后,具有名称的变量this_is_a_function将保持不变一个函数。

然而,R 充满了名称和函数:“->”是赋值函数的名称(一个保存赋值函数的变量)......

评估表达式后,您可以通过 验证这一点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 <- function(){}),同时过滤掉函数调用 ('+'(1,2), ...)。

我会说它是另一种方式:在那个表达式f中是变量(名称),它将被分配给函数(一旦调用被评估)。+(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"
于 2013-01-11T16:19:30.717 回答
3

与其查找函数定义(如果不实际评估函数就无法正确执行),查找函数调用会更容易。

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

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"       
于 2013-01-12T17:05:33.557 回答