Как проверить, является ли функция в R постоянной?

Мне передается функция R, которая определена на некотором интервале Real Line и возвращает числовое значение. Есть ли способ проверить, является ли функция постоянной?

Примеры функций:

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

Я ищу тест, который скажет, что f1, f3, f4 являются постоянными функциями, но f2 - нет. Есть идеи?

Edit:

Фрэнк и Грегор ( изменить: и второе решение Майкла Лоуренса) решения ниже всех работают для всех 4 приведенных выше тестовых случаев (Марат и Майкл не работают во всех четырех случаях). Так что уже есть решения. Но дополнительные бонусные баллы, если вы можете найти решение, которое также дает правильный ответ для следующих трех тестовых функций:

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

Ответы

Ответ 1

Эта функция проверяет, используется ли аргумент 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 не использует или не раскрывает "коды ошибок", которые позволяли бы интерпретировать язык-инвариантную интерпретацию a try.
    • Альтернатива, предложенная OP, будет просто проверять, была ли какая-либо ошибка, но я думаю, что это создало бы слишком много ложных срабатываний, если бы тестируемые функции имели какой-либо шанс на ошибку:

.

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

Ответ 2

Попробуйте functionBody:

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

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

Ответ 3

Эти тесты на основе кода являются умными и интересными, но до сих пор я думаю, что подход "попробуйте набор чисел" может быть более мощным тестом, в зависимости от типа функций, которые вы можете получить, и больше ли вы заботитесь о ошибках типа я или типа II в вашей идентификации.

В своем вопросе вы говорите

который определен на некотором интервале 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)

Ответ 4

Это обрабатывает такие случаи, как явный 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)

Ответ 5

Если вы притворяетесь, что эта функция является ее аналогом "mathworld", ответ на вопрос рекурсивно неразрешимый.