Нажать возврат к родительской функции

Есть ли способ заставить родительскую функцию возвращать вывод? Скажем, у меня есть функция, которая «что-то делает», и в начале каждой функции я хочу «что-то проверить». Если проверка не удалась, я хочу вернуть «что-то еще».

В моем примере ниже «что-то делает» — это логарифм, «проверка чего-то» означает проверку того, что переменная неотрицательна, а «что-то еще» равно минус бесконечности.

weird_log <- function(x) {
  check(x)
  log(x)
}

check <- function(x) {
  if (x <= 0)
    eval.parent(parse(text = 'return(-Inf)'))
}

Этот пример не работает

weird_log(10)  # 2.302585
weird_log(-10) # NaN

Одно из решений состоит в том, чтобы возвращать «что-то еще» из функции проверки, если проверки обнаруживают проблему, и NULL в противном случае. Затем я могу написать один if в родительской функции, и все готово.

weird_log <- function(x) {
  y <- check(x)
  if (!is.null(y)) return(y)
  log(x)
}

check <- function(x) {
  if (x <= 0) {
    -Inf
  } else {
    NULL
  }
}

Это решение по-прежнему сохраняет большую часть функциональности в отдельной функции check(), но есть ли способ сохранить в ней все функциональные возможности?


В реальной задаче функция проверки выполняет более одного сравнения и используется в нескольких функциях, поэтому ее необходимо иметь отдельно. Также «что-то еще», возвращающее функцию check, зависит от условия, при котором ввод не выполняется.


Более реалистичный пример:

weird_log <- function(input) {
  y <- check(input)
  if (!is.null(y)) return(y)
  list(log = log(input$x))
}

check <- function(input) {
  if (is.null(input$x)) {
    list(error = 'x is missing')
  } else if (!is.numeric(input$x)) {
    list(error = 'x is not numeric')
  } else if (x <= 0) {
    list(log = -Inf, warn = 'x is not positive')
  } else {
    NULL
  }
}

person Jan Kislinger    schedule 17.03.2017    source источник
comment
Зачем тебе вообще parse(text = 'return(-Inf)') там. Разве eval.parent(return(-Inf)) не сделает то же самое? Я не знаю, где вы, ребята, этому научились, но это действительно плохая практика написания кода.   -  person Rich Scriven    schedule 17.03.2017
comment
Хороший вопрос, я слишком усложнил его.   -  person Jan Kislinger    schedule 17.03.2017


Ответы (2)


Поскольку ответ на самом деле не отвечает на вопрос, как сделать то, что вы просили.

returnFromParent <- function() {
  call <- rlang::expr(return()) 
  rlang::eval_bare(call, env = parent.frame())
}

foo <- function(){
  returnFromParent()
  print("you should not see this")
}

foo()

Единственный способ, который я нашел для этого, - использовать rlang.

person JohnCoene    schedule 14.12.2020

KISS:

weird_log <- function(x) {
  if (check(x)) return(-Inf)
  log(x)
}

check <- function(x) {
  x <= 0
}

weird_log(10)  # 2.302585
weird_log(-10) # -Inf

Более распространенным является вариант использования, когда вы хотите выдать ошибку, когда проверка не удалась:

weird_log <- function(x) {
  check(x)
  log(x)
}

check <- function(x) {
  if(x <= 0) stop("x <= 0", call. = FALSE)
}

weird_log(10)  # 2.302585
weird_log(-10) # Error: x <= 0
person Roland    schedule 17.03.2017
comment
Я не указал это, но вывод проверки может отличаться в зависимости от нескольких внутренних условий. Так что проверка не может вернуть просто TRUE/FALSE. Я тоже не могу использовать stop(), ошибки для этой задачи недопустимы. - person Jan Kislinger; 17.03.2017
comment
Это не меняет моей точки зрения. Операторы возврата всегда должны быть частью функции, из которой вы хотите вернуться. check может, например, вернуть целочисленное значение, и вы можете использовать переключатель в родительской функции. - person Roland; 17.03.2017
comment
Это не ответ на вопрос. Подобные ответы на крошечные значки на самом деле бесят и наносят ущерб сообществу. - person JohnCoene; 14.12.2020
comment
Если вы думаете, что я забочусь о некоторых интернет-точках, вы глубоко ошибаетесь. Я забочусь о том, чтобы не пропагандировать плохую практику. - person Roland; 15.12.2020