【问题标题】:Is it possible to handle simple messages in R? If yes, how?是否可以在 R 中处理简单的消息?如果是,如何?
【发布时间】:2021-03-10 03:30:39
【问题描述】:

处理警告或错误可以使用

result = tryCatch({
    expr
}, warning = function(w) {
    warning-handler-code
}, error = function(e) {
    error-handler-code
}, finally = {
    cleanup-code
}

但是如果 expr 通过simpleMessage 给出一个message,我怎样才能得到它?有没有类似的?:

message = function(m) {message-handler-code}

或者其他可以让我捕获消息的功能?

谢谢!

【问题讨论】:

    标签: r exception try-catch


    【解决方案1】:

    tryCatch 是处理条件最常用的解决方案。

    但是,tryCatch中止执行并返回处理程序的值,而不是恢复代码的执行。这可能并不总是您想要的;有时你想处理一个条件并继续。

    R 允许这样做,这要归功于它的incredibly powerful condition system

    例如,您可以选择静音所有消息:

    suppressMessages(expr)
    

    这里的好处是suppressMessages 不是魔法——它是一个普通的旧 R 函数,你可以自己编写它(但你确实需要了解条件系统,它的多功能性的代价是它是相当复杂)。

    为了说明这一点,这里有另一种处理消息的方式——使用withCallingHandlers——它会抑制消息,继续执行调用它的代码,但同时将消息记录在一个列表中:

    messages = list()
    
    withCallingHandlers({
        message('Hello world')
        1 + 1
    }, message = function (msg) {
        messages <<- c(messages, msg)
        tryInvokeRestart('muffleMessage')
    })
    

    tryInvokeRestart('muffleMessage')suppressMessages 工作的方法相同。唯一的区别是我们添加了代码来存储消息。

    作为最后一步,上面的内容甚至可以包装在一个函数中:

    with_logging = function (expr) {
        messages = list()
    
        log_message = function (msg) {
            messages <<- c(messages, conditionMessage(msg))
            tryInvokeRestart('muffleMessage')
        }
    
        result = withCallingHandlers(expr, message = log_message)
        list(result = result, messages = messages)
    }
    

    并使用它:

    with_logging({
        message('this is a test')
        1 + 1
    })
    
    $result
    [1] 2
    
    $messages
    $messages[[1]]
    [1] "this is a test\n"
    

    【讨论】:

    【解决方案2】:

    是的,您可以像使用 warningerror 一样使用 message =

    result = tryCatch({
        message("Hello world")
        1 + 1
    }, message = function(m) {m}
    )
    
    result
    #> <simpleMessage in message("Hello world"): Hello world
    >
    

    但是,您更有可能希望分别捕获结果和消息:

    result <- withCallingHandlers({
        message("Hello world")
        1 + 1
    }, message = function(m) {
        lastMessage <<- m
        invokeRestart("muffleMessage")
    })
    
    result
    #> [1] 2
    
    if(exists("lastMessage")) message(lastMessage)
    #> Hello world
    

    【讨论】:

    • 很好,我们的思想融合了。
    • @KonradRudolph 是的,你有很好的展示。从教学的角度来说,您的答案是更好的答案,我希望它被 OP 接受。在调用环境中存储变量作为副作用,我仍然感到有些不安,并且想知道是否有更好的方法。看到你做同样的事情有点让人放心(尽管我想你是先明确地创建缓存,而不是之后测试是否存在)。
    • 如果封装得当,就没什么好担心的了,在使用闭包时,这是一种足够常见的模式。 R6 作为其实现的核心部分做同样的事情。然而,我使用&lt;&lt;- 只是为了简洁,在我自己的代码中我从不使用它。相反,我定义了对封闭环境的引用(简单地说,self = environment()with_logging 中),然后通过self$messages = c(messages, msg) 执行分配。
    • @KonradRudolph 同样,即使在 R 源代码中,我也很难找到可用重启的列表。文档非常简短。您不知道有一个简单的方法来列出它们吗?
    • 很遗憾,我认为不存在这样的列表。您可以枚举它们——参见findRestart 的源代码——但由于它们是动态创建的,除非在条件处理程序中,否则这将不起作用。要找到相关的、已定义的重新启动,您必须阅读条件信号器的代码以找出withRestarts 安装了哪些重新启动——在这种情况下,在message 内部。我假设你已经这样做了。也就是说,message 的文档 确实 提到了这次重启。其他条件帮助页面也是如此。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-24
    • 1970-01-01
    • 2018-04-23
    • 1970-01-01
    相关资源
    最近更新 更多