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

时间:2022-10-27 01:55:21

An R function is passed to me, which is defined on some interval of the Real Line and returns a numeric value. Is there a way to check to see if the function is constant?

一个R函数传递给我,它在Real Line的某个区间定义并返回一个数值。有没有办法检查功能是否恒定?

Example functions:

示例功能:

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

I am looking for a test that will say that f1,f3,f4 are constant functions but f2 isn't. Any Ideas?

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

Edit:

编辑:

Frank's and Gregor's (edit: and Michael Lawrence's second solution)solutions below all work for all the 4 test cases given above (Marat's and Michael's don't work on all 4 cases). So there is already solutions. But extra bonus points if you can find a solution which also gives the right answer for the following 3 test functions:

弗兰克和格雷戈尔(编辑:和迈克尔劳伦斯的第二个解决方案)以下解决方案都适用于上面给出的所有4个测试用例(Marat's和迈克尔不对所有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}

5 个解决方案

#1


5  

This function tests if the argument of f is used as a number:

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

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

Examples:

例子:

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

If you need to test whether a mathematical function is constant, you'll need special tools that understand and can simplify formulas.

如果你需要测试数学函数是否是常数,你需要特殊的工具来理解并简化公式。


Generality.

概论。

  • It doesn't make sense on functions with multiple arguments.
  • 它对具有多个参数的函数没有意义。
  • If using a different localization of R, ...
    • I'd suggest replacing or adding to the regex, e.g., with "(non-numeric argument)|(argument non numérique)". Unfortunately, as far as I can tell, R does not use or expose "error codes" that would allow for language-invariant interpretation a try result.
    • 我建议替换或添加正则表达式,例如,用“(非数字参数)|(参数nonnumérique)”。不幸的是,据我所知,R不使用或暴露“错误代码”,这将允许语言不变的解释成为尝试结果。
    • An alternative proposed by the OP would be simply checking whether there was any error, but I think that would create too many false positives if the functions being tested had any chance of bugginess:
    • OP提出的替代方案是简单地检查是否存在任何错误,但我认为如果被测试的函数有任何错误可能会产生太多的误报:
  • 如果使用R的不同本地化,...我建议替换或添加到正则表达式,例如,使用“(非数字参数)|(参数nonnumérique)”。不幸的是,据我所知,R不使用或暴露“错误代码”,这将允许语言不变的解释成为尝试结果。 OP提出的替代方案是简单地检查是否存在任何错误,但我认为如果被测试的函数有任何错误可能会产生太多的误报:

.

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

#2


11  

Try functionBody:

试试functionBody:

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

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

#3


5  

These code-based tests are clever and fun to see, but so far I think the "try a bunch of numbers" approach may be a more powerful test, depending on the type of functions you might get and whether you care more about Type I or Type II errors in your identification.

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

In your question, you say

在你的问题中,你说

which is defined on some interval of the Real Line

它是在Real Line的某个区间定义的

So let's assume we know the domain of interest. Sample some number of points on that domain, and test your function.

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

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

Any function that is truly a constant function will pass this test just fine, no matter how pathological---this will not be true for any of the other methods suggested so far. However, it is quite easy to fool the test with the example I left in the comments (or anything of this sort)

任何真正是常数函数的函数都会通过这个测试,无论多么病态 - 对于目前为止建议的任何其他方法都不行。但是,使用我在评论中留下的示例(或此类任何内容)来愚弄测试很容易

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

#4


5  

This handles cases like explicit return, missing { and even empty { }:

这会处理像显式返回,丢失{甚至空{}这样的情况:

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))

Here is another approach that is pretty smart, but it can be tricked. It will assume that any primitive function will return the same value, given constant arguments. It also allows symbols, as long as the symbols are defined within the function. But since symbols can be referenced before being defined, or defined but in a nested scope, that heuristic is not safe. Anyway, here it is:

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

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)))
}

Should be TRUE:

应为TRUE:

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

#5


3  

If you pretend that the function is its "mathworld" counterpart, the answer to the question is recursively undecidable.

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

#1


5  

This function tests if the argument of f is used as a number:

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

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

Examples:

例子:

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

If you need to test whether a mathematical function is constant, you'll need special tools that understand and can simplify formulas.

如果你需要测试数学函数是否是常数,你需要特殊的工具来理解并简化公式。


Generality.

概论。

  • It doesn't make sense on functions with multiple arguments.
  • 它对具有多个参数的函数没有意义。
  • If using a different localization of R, ...
    • I'd suggest replacing or adding to the regex, e.g., with "(non-numeric argument)|(argument non numérique)". Unfortunately, as far as I can tell, R does not use or expose "error codes" that would allow for language-invariant interpretation a try result.
    • 我建议替换或添加正则表达式,例如,用“(非数字参数)|(参数nonnumérique)”。不幸的是,据我所知,R不使用或暴露“错误代码”,这将允许语言不变的解释成为尝试结果。
    • An alternative proposed by the OP would be simply checking whether there was any error, but I think that would create too many false positives if the functions being tested had any chance of bugginess:
    • OP提出的替代方案是简单地检查是否存在任何错误,但我认为如果被测试的函数有任何错误可能会产生太多的误报:
  • 如果使用R的不同本地化,...我建议替换或添加到正则表达式,例如,使用“(非数字参数)|(参数nonnumérique)”。不幸的是,据我所知,R不使用或暴露“错误代码”,这将允许语言不变的解释成为尝试结果。 OP提出的替代方案是简单地检查是否存在任何错误,但我认为如果被测试的函数有任何错误可能会产生太多的误报:

.

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

#2


11  

Try functionBody:

试试functionBody:

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

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

#3


5  

These code-based tests are clever and fun to see, but so far I think the "try a bunch of numbers" approach may be a more powerful test, depending on the type of functions you might get and whether you care more about Type I or Type II errors in your identification.

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

In your question, you say

在你的问题中,你说

which is defined on some interval of the Real Line

它是在Real Line的某个区间定义的

So let's assume we know the domain of interest. Sample some number of points on that domain, and test your function.

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

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

Any function that is truly a constant function will pass this test just fine, no matter how pathological---this will not be true for any of the other methods suggested so far. However, it is quite easy to fool the test with the example I left in the comments (or anything of this sort)

任何真正是常数函数的函数都会通过这个测试,无论多么病态 - 对于目前为止建议的任何其他方法都不行。但是,使用我在评论中留下的示例(或此类任何内容)来愚弄测试很容易

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

#4


5  

This handles cases like explicit return, missing { and even empty { }:

这会处理像显式返回,丢失{甚至空{}这样的情况:

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))

Here is another approach that is pretty smart, but it can be tricked. It will assume that any primitive function will return the same value, given constant arguments. It also allows symbols, as long as the symbols are defined within the function. But since symbols can be referenced before being defined, or defined but in a nested scope, that heuristic is not safe. Anyway, here it is:

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

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)))
}

Should be TRUE:

应为TRUE:

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

#5


3  

If you pretend that the function is its "mathworld" counterpart, the answer to the question is recursively undecidable.

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