Как сообщить об ошибках в R?

Обновление: Вопрос закрыт, теперь обсуждается на платформе RStudio Community.


Я пытаюсь защищаться при разработке пакета, используя много проверок ввода. В частности, я полагаюсь на множество готовых утверждений в checkmate, testthat и т. П., Которые значительно облегчают жизнь (и делают код короче).

Руководство по стилю Tidyverse Хэдли Уикхемса для сообщений об ошибках предполагает, что сообщения об ошибках должны указывать пользователям точный источник проблемы, например так:

#> Error: Can't find column `b` in `.data`

(Столбцы - просто пример, иногда это могут быть строки или какой-то другой индекс).

Теперь мне интересно, как это можно элегантно и последовательно реализовать в пакете, учитывая, что многие из существующих утверждений (из вышеприведенного пакета, но также и из базы r) не дают вам никаких индексов в своих ошибках.

Вот пример:

m <- matrix(data = c(0, 1, 5, -2), nrow = 2)

# arbitrary assertion
assert_positive <- function(x) {
  if (any(x < 0)) {
    stop(call. = FALSE,
         "All numbers must be non-negative")
  } else {
    return(invisible(x))
  }
}
# (there are *lots* of these in packages such as checkmate, testthat or assertr that should be reused)

assert_positive(m)

дает:

## Error: All numbers must be non-negative

Пока все хорошо, но это не дает желаемых показателей ошибок.

Да, я знаю, что я мог бы просто изменить выше assert_positive() функция, чтобы сделать это, но я хотел бы повторно использовать многие функции в checkmate, testthat и друзей, так что я не могу их трогать, а их в любом случае слишком много.

Поэтому я, вероятно, должен обернуть что-то вокруг этих существующих тестов, например, простой цикл for:

# via for-loops
assert_positive2 <- function(x) {
  for (r in 1:nrow(x)) {
    res <- try(expr = assert_positive(x[r, ]), silent = TRUE)
    if (inherits(x = res, what = "try-error")) {
      stop(
        call. = FALSE,
        paste0(
          "in row ",
          r,
          ": ",
          attr(x = res, which = "condition")$message,
          "."
        )
      )
    }
  }
}

assert_positive2(m)

дает:

## Error: in row 2: All numbers must be non-negative.

Это делает работу, но это много беспорядка, и код не очень выразителен. Я также думал о Reduce() с try(), но это не даст индексы, и ни один не будет apply() действие. Я думаю, наконец, фабрика замыканий или функций была бы полезна, чтобы обобщить это на многие утверждения.

Это просто похоже на проблему, с которой уже столкнулись многие другие люди (создавая лучшие сообщения об ошибках), поэтому:

Какой элегантный / канонический способ сделать это?

Я знаю, что это не место для дискуссий и мнений; но это все еще лучший форум для такой проблемы, поэтому, пожалуйста, не закрывайте это.

1 ответ

Я не понимаю, как обернуть многие функции будет меньше, чем просто менять их / писать свои собственные версии. Плюс, как вы говорите, то, как вы завернули пример, совсем не мило.

В качестве краткого ответа, я мог бы представить, используя assertthat пакет (который вы не упомянули явно) и, в частности, функции assert_that() (для основных случаев) и on_failure() (для более широких пользовательских функций утверждений).

Я не думаю, что assert_positive Пример делает то, что вы хотите, поэтому, возможно, вам не следует пытаться переработать его. Точно так же assert_positive2 в других случаях может также не выполнять то, что вы хотите, потому что вы можете сообщить о конкретных индексах для каждой строки, которые нарушают, а не только о строках. Но с вашими собственными функциями вы можете написать что-то более гибкое, охватывающее несколько случаев.

Другие вопросы по тегам