【问题标题】:How to check to see if a function in R is a constant function?如何检查R中的函数是否为常数函数?
【发布时间】:2015-08-03 10:57:50
【问题描述】:

一个 R 函数被传递给我,它在实线的某个区间上定义并返回一个数值。 有没有办法检查函数是否为常数?

示例函数:

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}

【问题讨论】:

  • 你能做的最好的就是尝试一堆值。祝我好运f3 = function(x) ifelse(x == 5.46512616432116, 0, 1)
  • 不确定是否可以以编程方式执行任意函数。但是,如果您能说出上下文,也许有人可以提出解决方案。
  • 没错,Gregor,我希望能够捕捉到这样的功能。这就是为什么我希望有一个完全不测试一堆值的程序解决方案。因为 R 知道函数,所以理论上信息就在那里......
  • 但是如果你的域是 0 到 5,我的函数是一个常数函数。为了解决这个问题,我认为你需要能够做出更有力的假设。这些函数是如何创建的?你有固定域名吗?函数有多复杂,它们可以调用其他函数吗?我还可以编写一个看起来很复杂但始终不变的非常钝的函数。
  • 如果要测试代码是否使用x作为数字,只需传递一个非数字即可享受错误:f1("a"); f2("a")。正如 Gregor 所说,这不会捕获恒定性是数学而非程序性的情况,例如f3 &lt;- function(x){x-x}。为此,Mathematica 也许?我听说它可以简化公式...

标签: r function constants


【解决方案1】:

这可以处理诸如显式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))

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

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)

【讨论】:

  • 我尝试了您的功能,但问题中的 f2 失败了。它给出了 true 而它应该是 false ...... > evaluatesToConstant(f2) [1] TRUE > f2 function(x) {x+2}
  • 对不起。我的错,我没有意识到我应该做 evaluatesToConstant(functionBody(fun)) 而不仅仅是 evaluatesToConstant。
  • 是的,因为要支持+ 之类的东西,我们需要将在给定常量参数时总是返回相同结果的函数调用列入白名单。我想这可能适用于任何原始人。我将为答案添加不同的方法。
【解决方案2】:

如果你假装这个函数是它的“数学世界”对应物,那么这个问题的答案是递归不可判定的。

【讨论】:

    【解决方案3】:

    此函数测试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 没有使用或公开允许语言不变解释 try 结果的“错误代码”。
      • OP 提出的替代方案是简单地检查是否存在 any 错误,但我认为如果正在测试的函数有任何错误的机会,这会产生太多误报:

    .

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

    【讨论】:

    • 谢谢!这很好,但只有当您的系统语言是英语时它才有效;)
    • 例如我得到:try(f2("Hello World!")) Error in x + 2 : argument non numérique pour un opérateur binaire
    • 如果解决方案被修改为简单地捕获错误会更好。
    • @bisounours_tronconneuse 是的,我同意。我不习惯捕捉错误,所以现在正在研究如何修改它。但是,很高兴知道我们正在捕获正确的错误......就像错误代码一样,假设 R 有这些。
    • 所以类似 is_using_argasnumber
    【解决方案4】:

    这些基于代码的测试既聪明又有趣,但到目前为止,我认为“尝试一堆数字”方法可能是更强大的测试,具体取决于您可能获得的函数类型以及您是否更关心关于您的身份识别中的 I 类或 II 类错误。

    在你的问题中,你说

    在实线的某个区间上定义

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

    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
    

    任何真正是常量函数的函数都可以很好地通过这个测试,无论多么病态——这对于迄今为止建议的任何其他方法都不是真的。但是,很容易用我留在 cmets 中的示例(或任何此类)来欺骗测试

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

    【讨论】:

    • 我仍然不确定 OP 是否正在寻找函数属性或数学属性测试,但我喜欢后者的解决方案,至少以“我如何测试”的形式如果我的实数紧凑子集中的实值函数是常数?”
    • 而且,如果函数不是以某种方式“随机”生成的,也很容易检查“特殊”值,如果它包含在域中,则为 0,可能检查 pretty(mydomain) 中的值.
    【解决方案5】:

    试试functionBody:

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

    【讨论】:

    • 谢谢!!这不是万无一失的,例如 f4 = function(x) ifelse(x == 5.46512616432116, 0, 0) 出现不是恒定的,即使它是。但它已经足够接近了。
    • @bisounours_tronconneuse - 因为这在技术上是一个函数调用,而不是一个常量。它取决于另一个变量:)
    • @bisounours_tronconneuse,试试f3 &lt;- function() { 1+1 }。我认为最简单的;)
    • 您可以将eval 添加到 Marat 的答案中,以确保进行评估,因为 1+1 也是一个函数调用。喜欢is.numeric(eval(functionBody(f3)[[2]]))
    • 一旦有函数调用,所有的赌注都被取消了。这个答案的一个容易解决的问题是,它需要强大到没有{。更好的检查是!is.language() 而不是is.numeric()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-10
    • 1970-01-01
    相关资源
    最近更新 更多