如何检查 R 中的函数是否为常数函数?

How to check to see if a function in R is a constant function?

一个R函数传给我,它定义在实线的某个区间和returns一个数值。 有没有办法检查函数是否恒定?

示例函数:

f1<-function(x) {11}
f2<-function(x) {x+2}
f3<-function(x) {1+1}
f4<-function(x) {return(3)}

我正在寻找一个测试,它会说 f1、f3、f4 是常数函数,但 f2 不是。有任何想法吗?

编辑:

Frank 和 Gregor(编辑: 和 Michael Lawrence 的第二个解决方案)下面的解决方案适用于上面给出的所有 4 个测试用例(Marat 和 Michael 的不适用于所有 4 个个案)。所以已经有了解决方案。但是,如果您能找到一个解决方案,该解决方案也为以下 3 个测试函数提供了正确答案,则额外加分:

f5 <- function(x) ifelse(x == 5.46512616432116, 0, 1)
f6 <- function(x) ifelse(x == 5.46512616432116, 0, 0)
f7 <- function(x) {x - x}

尝试 functionBody:

> is.numeric(functionBody(f1)[[2]])
[1] TRUE

> is.numeric(functionBody(f2)[[2]])
[1] FALSE

此函数测试 f 的参数是否用作数字:

is_using_argasnumber <- function(f) 
  grepl("non-numeric argument",try(f("Hello World!"),silent=TRUE))

示例:

is_using_argasnumber(function(x)1+1)        # FALSE
is_using_argasnumber(function(x)"guffaw")   # FALSE
is_using_argasnumber(function(x)sqrt(x+2))  # TRUE

如果您需要测试数学函数是否恒定,您将需要能够理解并简化公式的特殊工具。


一般性。

  • 它对具有多个参数的函数没有意义。
  • 如果使用不同本地化的 R,...
    • 我建议替换或添加正则表达式,例如 "(non-numeric argument)|(argument non numérique)"。不幸的是,据我所知,R 不使用或公开 "error codes" 允许语言不变解释 try 结果。
    • OP 提出的替代方案是简单地检查是否存在 any 错误,但我认为如果被测试的函数有任何机会越野车:

.

is_breaking_withargascharacter <- function(f)
  inherits(try(f("Hello World!"),silent=TRUE),'try-error')

这些基于代码的测试很聪明,看起来也很有趣,但到目前为止,我认为 "try a bunch of numbers" 方法可能是更强大的测试,具体取决于您可能获得的函数类型以及您是否更关心关于您身份识别中的 I 类或 II 类错误。

在你的问题中,你说

which is defined on some interval of the Real Line

所以让我们假设我们知道感兴趣的领域。在该域上采样一些点,并测试您的功能。

n = 1e5
test = runif(n, min = 0, max = 5)
results = f(test) # sapply(test, f) if f isn't vectorized

# test for constancy
all(results == results[1]) # or all(diff(results) == 0) or however else

任何真正是常量函数的函数都可以很好地通过这个测试,无论多么病态——这对于目前建议的任何其他方法都是不正确的。然而,用我在评论中留下的例子(或任何类似的东西)来愚弄测试是很容易的

f3 = function(x) ifelse(x == 5.46512616432116, 0, 1)

如果您假设该函数是它的 "mathworld" 对应函数,则问题的答案是递归不可判定的。

这可以处理显式 return、缺少 { 甚至空 { }:

等情况
evaluatesToConstant <- function(b) {
    if (is(b, "{")) {
        if (length(b) > 2L)
            return(FALSE)
        if (length(b) == 1L)
            last <- NULL
        else last <- b[[2L]]
    } else {
        last <- b
    }
    if (is.call(last) && last[[1L]] == quote(return)) {
        last <- last[[2L]]
    }
    !is.language(last)
}
evaluatesToConstant(functionBody(fun))

这是另一种非常聪明的方法,但它可以被欺骗。它将假设任何原始函数都将 return 相同的值,给定常量参数。它还允许符号,只要符号是在函数内定义的。但是由于可以在定义之前引用符号,或者在嵌套范围内定义符号,因此这种启发式方法并不安全。无论如何,这里是:

evaluatesToConstant <- function(expr, allowDefinitions=FALSE) {
    vars <- all.vars(expr)
    calls <- setdiff(all.names(expr), vars)
    funs <- mget(calls, parent.frame(), mode="function", inherits=TRUE)
    defined <- if (allowDefinitions)
                   rapply(as.list(expr),
                          function(x) as.character(substitute(x)[[2L]]), "<-",
                          how="unlist")
    length(setdiff(vars, defined)) == 0L &&
        all(vapply(funs, is.primitive, logical(1L)))
}

应该是TRUE:

evaluatesToConstant(functionBody(function(x) { foo <- 1 + 1; foo }), TRUE)