【问题标题】:Check if a quosure contains a string检查quosure是否包含字符串
【发布时间】:2021-04-19 16:30:00
【问题描述】:

当我第一次学习 NSE 时,我有一个函数用于接受不带引号的输入,现在我讨厌这个用例。我想强制该函数只接受一个字符串,但向用户提供一个明确的错误消息。

我的问题是测试正在传递的输入类型。我可以使用rlang::quo_is_symbol(rlang::enquo(str_x)),但是将多个函数链接在一起时会失败。这是我在代表处的剪辑。

fun1 <- function(str_x) {
  depr_str_x(str_x)
  paste("this is x", str_x)
}

depr_str_x <- function(str_x) {
  
  if (rlang::quo_is_symbol(rlang::enquo(str_x))) {
    stop("this is a clear error message to the user")
  }
  
  invisible(NULL)
}

所以当我运行它时,我得到了我想要的。

depr_str_x("test") # nothing (which I want)
depr_str_x(test)   # my custom error (which I want)

但是当我运行它时,我两次都得到错误。我如何让它认识到正在传递的"test" 是一个字符串?我也尝试过rlang::is_scalar_character(),但对于符号大小写失败了。我希望有一种快速简便的方法可以做到这一点,而无需强制评估并查看它是否失败或其他什么。

fun1("test")       # my custom error (which I don't want)
fun1(test)         # my custom error (which I want)

此外,当该函数在其他函数内部使用时,这应该可以工作。这就是我不断遇到问题的地方。

fun2 <- function(str_x) fun1(str_x)

fun2("test")       # my custom error (which I don't want)
fun2(test)         # my custom error (which I want)

【问题讨论】:

  • 我不希望未引用的工作。我明确地试图阻止它。这就是我以前拥有它的方式,但我想删除该功能,因为它会导致不好的做法,因为这个包会在其他包中使用。它导致发生其他错误。但我不想在不显示明显错误的情况下将其删除。
  • 我更新了帖子,但我只是想知道错误消息中的前缀部分是否重要是depr_str_x(!!str_x)而不是depr_str_x(test)
  • 不,我想你得到了我想要的。那会很好。我正在处理你的更新。

标签: r rlang


【解决方案1】:

我通过粗略地捕获错误object 'x' not found 使其正常工作(但我不确定这是否不会出现错误)。我不回答这个问题,欢迎更好的解决方案。

depr_str_x <- function(str_x) {
  
  is_valid <- tryCatch(rlang::is_scalar_character(str_x),
                     error = function(e) {
                       if (grepl("not found", e, fixed = TRUE)) {
                         return(FALSE)
                       } else {
                         stop(e)
                       }
                     })
  
  if (!is_valid)
    stop("this is a clear error message to the user")
  
}

【讨论】:

    【解决方案2】:

    NSE 参数和可能是也可能不是 NSE 的参数要求 quasiquotation 正确地从一个函数传递到下一个函数。最直接的方法是使用{{

    fun1 <- function(str_x) {
      depr_str_x({{str_x}})                   # <-- Note the {{ operator
      paste("this is x", str_x)
    }
    
    fun1("test")   # [1] "this is x test"
    fun1(test)     # Error in depr_str_x({ : this is a clear error message to the user 
    

    fun2 也是如此:

    fun2 <- function(str_x) fun1({{str_x}})   # <---- Ditto on {{
    
    fun2("test")   # [1] "this is x test"
    fun2(test)     # Error in depr_str_x({ : this is a clear error message to the user 
    

    编辑:要记住的是,通过允许可能是也可能不是 NSE 的参数,您正在制造歧义。考虑一下,

    test <- "This is a string"
    fun2(test)                    # What should happen here?
    

    对于您的情况,我的一般建议是完全放弃 NSE 支持并记录更改。然后 NSE 输入将自动失败,但会显示“未找到变量测试”的错误,这应该足以让大多数用户找出问题所在。

    【讨论】:

    • 我的问题实际上是因为我做了你在最后一段中推荐的事情:)。我正在放弃 NSE 支持并尝试找到一种快速简便的方法来将函数添加到其他函数中,如果它不是标量字符,则会抛出 deprecation_stop()。我的问题是在从各种其他函数传递 NSE 输入的情况下找不到整个对象。
    • 我可能误解了用例,但“找不到对象”是不支持 NSE 的函数的预期行为。添加deprecation_stop()并不是NSE的真正下降,因为它需要NSE来触发它,这意味着调用deprecation_stop()的函数必须接受未评估的参数。基本上,您的两个选项是 1)让“找不到对象”发生,或 2)使用 {{ 传递未评估的参数,因此它们到达 deprecation_stop()
    • 你确实解决了我的问题。我基本上是想弄清楚我是否在 quosure 中捕获了一个有效的对象(类型字符)。不是,很可能是因为用户输入了一个未引用的对象,我想清楚地告诉他们该怎么做。因此,我所做的解决方法是捕获“找不到对象”错误并将其替换为我选择的另一个错误。也许这就是必须要做的?我很欣赏你对此的想法。我希望我能“知道”它从来都不是一个字符串,但似乎我必须在那时跟踪整个调用堆栈。
    猜你喜欢
    • 1970-01-01
    • 2011-11-09
    • 2013-05-18
    • 2012-06-30
    • 1970-01-01
    • 2013-10-26
    • 1970-01-01
    • 2013-11-20
    • 2015-01-04
    相关资源
    最近更新 更多