我尝试使用rlang包来构造一个执行赋值的表达式,给定一个右侧表达式(要赋值的值)和一个左侧表达式(将其赋值给它的地方) )。例如,让我们说我想构建和评估表达式a <- 5
:
> library(rlang)
> a <- "Not 5"
> lhs <- quo(a)
> rhs <- quo(5)
> eval_tidy(quo( (!!lhs) <- (!!rhs)) ) # Error
Error in (~a) <- (~5) : could not find function "(<-"
> eval_tidy(quo(`<-`(!!lhs, !!rhs))) # Error
Error in ~a <- ~5 : could not find function "~<-"
> eval_tidy(quo(`<-`(!!f_rhs(lhs), !!rhs))) # No error, but no effect
[1] 5
> stopifnot(a == 5)
Error: a == 5 is not TRUE
> print(a)
[1] "Not 5"
正如您所看到的,上述构造和评估此分配的方法都没有达到预期的效果。有没有办法正确地做到这一点?
修改:使用assign
代替<-
不是一个好方法,因为它只适用于变量,而不适用于对象元素。例如,它不会为:
> a <- list(ShouldBeFive="Not 5")
> lhs <- quo(a$ShouldBeFive)
编辑2:我写了proof of concept来演示我想要完成的任务。它定义了assign_general
函数,允许任意左侧,例如assign_general(a[[1]], 5)
相当于a[[1]] <- 5
。然而,我的实施似乎有点hackish,我不知道我可能错过了什么角落,我仍然不确定是否有更直接的方式来做,所以我&#39 ; m仍然有兴趣看看是否有人有更好的解决方案。
答案 0 :(得分:1)
1)rlang :: lang 我们可以像这样使用rlang::lang
:
library(rlang)
# inputs
a <- "Not 5"
lhs <- quote(a)
rhs <- 5
L <- lang("<-", lhs, rhs)
eval(L)
a
## [1] 5
2)致电或rlang
使用call
代替lang
:
# inputs
a <- "Not 5"
lhs <- quote(a)
rhs <- 5
cc <- call("<-", lhs, rhs)
eval(cc)
a
## [1] 5
2a)以上两点也适用于lhs
是适当表达式的情况。例如,使用内置数据框BOD
:
# inputs
BOD2 <- BOD
lhs <- quote(BOD2$xyz)
rhs <- 5
cc <- call("<-", lhs, rhs)
eval(cc)
names(BOD2)
## [1] "Time" "demand" "xyz"
2b)assign_general
assign_general <- function(lhs, rhs, envir = parent.frame()) {
cc <- call("<-", substitute(lhs), substitute(rhs))
eval(cc, envir = envir)
}
# test
a <- 1:5
assign_general(a[3], 5)
a
## [1] 1 2 5 4 5
call
语句的一些替代方法是:
cc <- substitute(call("<-", lhs, rhs))
或
cc <- substitute(lhs <- rhs)
2c)当然这就足够了:
assign_general2 <- `<-`
a <- 1:5
assign_general2(a[3], 5)
## [1] 1 2 5 4 5
3)assign_general的rlang版本可以通过将assign_general
替换为call
和{{1}来获取(2b)中lang
的rlang实现与substitute
:
enexpr
4)字符串另一种可能性是将参数解析为字符串:
library(rlang)
assign_general3 <- function(lhs, rhs, envir = parent.frame()) {
L <- lang("<-", enexpr(lhs), enexpr(rhs))
eval(L, envir = envir)
}
# test
a <- 1:5
assign_general3(a[3], 5)
a
## [1] 1 2 5 4 5
答案 1 :(得分:0)
元编程的优点是使用表达式作为字符串,并且能够执行L值的赋值,L值也可以声明为字符串。在某些情况下,赋值函数可以在元编程中重用。
rhs <- "1 > 0"
assign("lhs", eval(eval_tidy(parse(text=rhs))))
lhs
[1] TRUE
上面你可以看到lhs和rhs都作为字符串传递,表达式被赋值给L值。
答案 2 :(得分:0)
带着一点黑魔法和一些运气,我能够实现你的目标:
library(rlang)
expr<-quote(x<-1) # just some sample assignment operator to modify
a <- list(ShouldBeFive="Not 5")
lhs <- quo(a[[1]])
rhs <- quo(5)
expr[[2]] <-UQE(eval(lhs))
expr[[3]] <-UQE(eval(rhs))
expr
>a[[1]] <- 5
eval(expr)
a$ShouldBeFive
>5
这里有希望更清洁的替代品,不依赖于rlang
:
b <- list(ShouldBeSix="Not 6")
lhs <- quote(b[[1]])
rhs <- quote(6)
eval(substitute(x <- value,list(x = lhs, value = eval(rhs))))
b$ShouldBeSix