2017-07-12 42 views
1

我試圖使用rlang包構造一個表達式,該表達式賦予一個右側表達式(要賦值的值)和一個左側表達式(指定給它的位置)的賦值。例如,假設我要構建和評價表達a <- 5是否可以使用rlang構造賦值表達式?

> 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(a[[1]], 5)相當於a[[1]] <- 5。然而,我的執行看起來有些ha,,我不知道我可能錯過了哪些角落案例,而且我仍然不確定是否有更直接的方式來執行此操作,所以我仍然有興趣查看是否有人有更好的解決方案。

+0

可你只需要使用'assign'? – Dason

+0

'assign'在任意左側不起作用。如果'lhs < - quo(a [[1]])'怎麼辦? –

+0

我可以問爲什麼你要使用rlang嗎? – Dason

回答

0

元編程的優點是使用表達式作爲字符串並能夠執行賦值給L值的賦值,這也可以聲明爲字符串。在某些情況下,分配函數可以在元編程中重用。

rhs <- "1 > 0" 
assign("lhs", eval(eval_tidy(parse(text=rhs)))) 
lhs 
[1] TRUE 

上面你可以看到,lhs和rhs都作爲字符串傳遞,並且表達式被分配給一個L值。

+0

正如評論中提到的那樣,「assign」不夠一般。與'<--'不同,它不能分配給列表中的元素或對象的其他部分。它只能分配給環境中的變量名稱。 –

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 
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)調用或不使用rlangcall代替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版本的rlang執行(2b)中可以得到assign_general通過用langsubstitute與替換callenexpr

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 

4)串另一種可能性是參數deparse成字符串:

assign_general4 <- function(lhs, rhs, envir = parent.frame()) { 
    s <- paste(deparse(substitute(lhs)), "<-", deparse(substitute(rhs))) 
    p <- parse(text = s) 
    eval(p, envir = envir) 
} 

# test 
a <- 1:5 
assign_general4(a[3], 5) 
a 
## [1] 1 2 5 4 5 
相關問題