如何解决“ fun <-”通过评估“ value”开始的事实?

时间:2019-02-07 13:16:24

标签: r parameter-passing lazy-evaluation assign

考虑以下函数,如果条件为Position,它将用值替换lhs

TRUE

如果`==<-` <- function (e1, e2, value) replace(e1, e1 == e2, value) 将x替换为42:

x == 3

到目前为止很好,但是如果x <- 3 x == 3 <- 42 x # [1] 42 有副作用怎么办?到目前为止,即使我的病情是value,它也会被评估。

FALSE

有没有解决的办法?

请参阅以下有关此问题的解决方法,但是我想看看是否还有更多解决方法。


编辑:

这解决了sotos的评论:

# desired: if x == 100, stop
x == 100 <- stop("equals 100!")
# Error: equals 100!

1 个答案:

答案 0 :(得分:2)

以下是解决此问题的几种方法:

  1. quote并修改==<-,以便它始终评估引用的呼叫
  2. 使用~作为引用函数
  3. 使用~作为函数的简写,并使用rlang::as_function
  4. 使用函数delay引用输入并添加类delayed,以便仅对未引用的输入和delayed引用的输入进行评估。
  5. 优先使用<-来识别==<-,并始终delay lhs

最后一种方法是唯一一种无需更改接口即可使用的方法,尽管它的工作原理是覆盖<-,这通常是不建议的。

1。 quote并修改==<-,以便它始终评估引用的呼叫

如果我们知道我们不想分配未评估的通话 我们可以确保我们的函数评估所有内容,然后引用 输入。

`==<-` <- function (e1, e2, value) {
  cond <- e1 == e2
  if(any(cond)) 
    replace(e1, e1 == e2, eval.parent(value))
  else e1
}

x <- 42
x == 100 <- quote(stop("equals 100!"))
x <- 100
x == 100 <- quote(stop("equals 100!"))
# Error in eval(expr, envir, enclos) : equals 100! 

2。使用~作为报价功能

如果我们知道我们不想分配公式  我们可以使用~代替引号。

`==<-` <- function (e1, e2, value) {
  cond <- e1 == e2
  if(any(cond)) 
    replace(e1, e1 == e2,
            if(inherits(value, "formula")) 
              eval.parent(as.list(value)[[2]])
            else
              value)
  else e1
}


x <- 42
x == 100 <- ~stop("equals 100!")
x <- 100
x == 100 <- ~stop("equals 100!")
# Error in eval(expr, envir, enclos) : equals 100! 

3。使用~作为函数的简写,并使用rlang::as_function

如果我们不想分配函数或公式,则可以更进一步,并从中构建功能。

`==<-` <- function (e1, e2, value) {
  cond <- e1 == e2
  if(any(cond)) 
    replace(e1, e1 == e2,
            if(inherits(value, "formula") || is.function(value)) 
              rlang::as_function(value)(e1)
            else
              value)
  else e1
}

x <- 42
x == 100 <- ~stop("equals 100!")
x <- 100
x == 100 <- ~stop("equals 100!")
# Error in eval(expr, envir, enclos) : equals 100! 
x == 100 <- sqrt
x
# [1] 10

4。使用函数delay引用输入并添加类delayed

我们可以创建一个函数delay,该函数将quote的{​​{1}}表达式,并添加一个类value,我们的函数将其识别到"delayed"处的调用中合适的时机:

trigger

优点是它可以与可能触发错误的任何代码一起工作,缺点是`==<-` <- function (e1, e2, value) { cond <- e1 == e2 if(any(cond)) replace(e1, e1 == e2, if (inherits(x,"delayed")) eval.parent(x) else x) else e1 } delay <- function(x) { x <- substitute(x) class(x) <- "delayed" x } x <- 42 x == 100 <- delay(stop("equals 100!")) x <- 100 x == 100 <- delay(stop("equals 100!")) # Error in eval(expr, envir, enclos) : equals 100! 是一个奇怪的函数,仅在特定上下文中才有意义。

我们可以通过参考包装帮助定义适当的打印方法来减轻尴尬:

delay

我们可以使用相同的原理设计一个print.delayed <- function(x,...){ message( "Delayed call, useful as a `value` argument of `mmassign` assignment functions.\n", "See ?mmassign::delay.") print(unclass(x),...) x } delay(stop("equals 100!")) # delayed call, useful as a `value` argument of `mmassign` assignment functions. # See ?mmassign::delay. # stop("equals 100!") 函数,该函数的行为将“延迟”

STOP

5。覆盖STOP <- function(...) `class<-`(substitute(stop(...)), "delayed") x <- 42 x == 100 <- STOP("equals 100!") x <- 100 x == 100 <- STOP("equals 100!") # Error in eval(expr, envir, enclos) : equals 100! STOP("equals 100!") # delayed call, useful as a `value` argument of `mmassign` assignment functions. # See ?mmassign::delay. # stop("equals 100!") 以识别<-,并且总是==<- lhs

如果我们重写delay可以使它起作用,但这当然不是一个好习惯,所以只是为了好玩。如果LHS的第一个元素是<-,则引用值并添加类==并按上述步骤进行。

"delayed"