【问题标题】:R return object invisibly together with normal error when error is thrown抛出错误时,R将对象与正常错误一起隐形返回
【发布时间】:2021-03-10 23:16:35
【问题描述】:

我有一个可能会引发错误的函数。当抛出错误时,我想显示错误消息,就好像错误确实发生了一样,并进一步无形地返回一个对象。

我查看了this thread,它使用withCallingHandlers 并在某处记录错误消息。这很接近,但我不想将消息记录为文本然后打印文本消息,该函数应该显示错误消息,就好像它会在错误时退出一样。

函数工作流程如下所示:

foo <- function(x){

  y <- x + 1
  
    if(y == 2) {
    stop("oops")
    # also return y invisibly when error is thrown,
  }
  
  z <- y + 1
  z
  
}

根据输入x 计算中间yy 用于错误检查。如果发生错误,y 应该不可见地返回,并且应该抛出一个正常的错误消息。否则 z 被计算并返回。

foo(1) 应该返回错误消息和y 不可见。

我曾考虑过使用on.exit,但在这种情况下,y 总是隐形返回。

感谢任何帮助。

补充:也许我的想法是不可能的。在这种情况下,是否可以以接近真实错误消息的方式显示记录的错误消息?

Add2:我考虑过发出警告,但在我的实际用例中,warning 会产生误导,因为该函数不会产生所需的结果z,而只是一些中间结果y,我想返回y 以便用户可以进一步检查它,并说明foo 未正确处理它的原因。想一想,其他人肯定也遇到过同样的问题,应该有什么解决办法吧。

Add3:也许可以将on.exit与触发的标志一起使用,这样on.exit将在出现错误时不可见地返回y,否则什么都不做。

【问题讨论】:

  • 对我来说,“throw an error” 意味着函数在无法继续的前提下故意放弃控制。然后返回一个值与此相反。您是否有可能想要记录一个错误然后返回一些东西,但是您提到withCallingHandlers,听起来这可能不是您需要的。
  • 我担心我的想法是不可能的。在这种情况下,是否可以以接近真实错误消息的方式显示记录的错误消息?
  • 您可以发出(严厉的?)warning,其显示类似于错误。由于控制没有被破坏,你可以警告然后return
  • 抱歉删除了我之前的评论,因为不可见......这样做就足够了if(y == 2) { tryCatch(stop("oops"), error=function(.) message(conditionMessage(.))) return(invisible(y)) }。虽然在错误消息上仍然不太正确
  • @user20650:再次感谢您的方法!我将它与 r2evans 的答案结合起来,一旦结合起来,这完全解决了我的问题。

标签: r error-handling


【解决方案1】:
func <- function(x) { a <- simpleError("quux"); attr(a,"abc") <- 7; stop(a); }
func()
# Error: quux

到目前为止一切顺利,我们看到了一个错误。如果我们抓住这个并查看错误消息的内容,我们可以看到隐藏在里面的属性:

dput(tryCatch(func(), error=function(e) e))
# structure(list(message = "quux", call = NULL), class = c("simpleError", 
# "error", "condition"), abc = 7)

甚至可以轻松提取

dput(tryCatch(func(), error=function(e) attr(e,"abc")))
# 7

【讨论】:

  • 所以对于我的函数foo,我们将让它返回simpleError: quux&gt;,然后我们可以创建一个自定义函数来使用.Last.value 检查该错误。这是一个很好的方法。
  • 再次感谢您推荐simpleError。我唯一遇到的问题是simpleError 的“非标准”错误消息,它看起来与常规错误不同。我现在将您的方法与@user20650 的方法结合使用(正如他在我的问题中提到的那样)。我在下面发布了最终答案。
  • 虽然您可以选择任何可行的方法,但我不明白您所说的“非标准”错误消息是什么意思:我们使用simpleError 生成结构,添加属性,然后stop 与它。该错误消息看起来与没有属性的stop("quux") 完全相同。
  • 也许我想得太复杂了。我单独使用stop 尝试了它,而不依赖于user20650 的解决方法,但我无法获得与下面的答案相同的结果。如果您可以使用stop 展示如何操作,我很乐意接受这个答案。这三个目标是 (i) foo(1) 像常规错误一样以红色返回错误消息,(ii) 我们可以从错误中提取包含 y 的属性,以及 (iii) foo(1) 的结果应该是类 error 或类似的。
  • 如果您的解决方案有效,请坚持下去,我不会为分数而卑躬屈膝。这是一个有趣的问题。
【解决方案2】:

下面我发布我对我的问题的最终解决方案。基本上它是基于 r2evans 的回答和 user20650 的评论。

正如 r2evans 所示,我们可以使用simpleError 来创建错误并在属性中附加一个对象。这种方法的最大优点是它返回一个我们可以编程的真实错误。如果我们只显示错误消息然后不可见地返回一个向量,tryCatch 不会将其识别为错误。缺点是simpleError 在控制台中打印时对于交互式用户来说看起来不像是正常错误。它将显示类似&lt;simpleError: x must not be 1&gt; 的内容。但是,消息不会像正常错误那样以红色打印。

这里 user20650 的评论显示了一个不错的出路。我们可以先用message(conditionMessage(e))打印消息,然后我们可以不可见地返回simpleError

foo <- function(x) {
  
  y <- x + 1

  if(y == 2) {
    foo_internal <- function(val) {
      a <- simpleError("x must not be 1")
      attr(a,".data") <- y
      stop(a)
    } 
    return(
      tryCatch(foo_internal(y),
               error = function(e) {
                message(conditionMessage(e))
                invisible(e)
                })
           )
  }
  
  z <- y + 1
  z
  
}

# this is a special function to inspect the object attached to `simpleError`
inspect <- function(x = NULL) {
  if(is.null(x)) {
    x <- .Last.value
  }
  attr(x,".data")
}

# returns error message
foo(1)
#> x must not be 1

# we can get y's value with a special inspect function 
inspect()
#> [1] 2

# foo(1) returns a "real" error
class(foo(1))
#> x must not be 1
#> [1] "simpleError" "error"       "condition"

reprex package (v0.3.0) 于 2021-03-13 创建

【讨论】:

    猜你喜欢
    • 2018-01-06
    • 1970-01-01
    • 2014-03-15
    • 1970-01-01
    • 2015-12-26
    • 2018-08-31
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多