为什么 Tidyeval 引用在 lambda 中失败?
Why do Tidyeval quotes fail in lamdas?
下面是一个简单示例,说明如何使用引号动态重命名 tibble 列。
quoteExample = function() {
new_name = quo("new_name_value");
tibble(old_name=list(1,2,3)) %>%
rename( !! quo_name(new_name) := old_name)
}
quoteExample()
结果= tibble(new_name_value=list(1,2,3))
除了这次在 lamda 中,下面是同一个简单示例。
{function ()
new_name = quo("new_name_value");
tibble(old_name=list(1,2,3)) %>%
rename( !! quo_name(new_name) := old_name)
} ()
结果= is_quosure 中的错误(现状):未找到对象 'new_name'
为什么引号在 lamda 中失败但在命名函数中却没有?这种差异从何而来?难道我做错了什么?
编辑:上面的例子已经被 Akrun 解决了,但是下面是另一个例子,尽管已经应用了建议的解决方案,但还是失败了:
df = tibble(data=list(tibble(old_name= c(1,2,3))))
df %>%
mutate(data = map(data, (function(d){
new_name = quo("new_value")
d %>% rename( !! quo_name(new_name) := old_name)
})))
结果:is_quosure 中的错误(现状):未找到对象 'new_name'
失败是因为另一个问题吗?
如果我们用 ()
或 {}
使其独立,它应该可以工作
(function() {
new_name = quo("new_name_value");
tibble(old_name=list(1,2,3)) %>%
rename( !! quo_name(new_name) := old_name)
})()
# A tibble: 3 x 1
# new_name_value
# <list>
#1 <dbl [1]>
#2 <dbl [1]>
#3 <dbl [1]>
如果匿名函数只包含一个expr
ession,则不需要使用{}
,但如果表达式多一行,则用{}
换行.根据?body
The bodies of all but the simplest are braced expressions, that is calls to {: see the ‘Examples’ section for how to create such a call.
这与 基本上是同一个问题。主要原因是 !!
运算符强制立即评估其参数, 在 创建匿名函数环境之前。在您的情况下, !! quo_name(new_name)
尝试找到 new_name
相对于整个表达式(即整个 mutate(...)
表达式)的定义。由于 new_name
是在表达式本身中定义的,因此您最终会遇到导致 "object not found" 错误的循环依赖。
你的三个选择是
1) 将您的 lambda 拉出到一个独立的函数中,以确保首先创建它的环境,从而在 !!
运算符强制其评估之前正确初始化该环境中的所有变量:
f <- function(d) {
new_name = sym("new_value")
d %>% rename(!!new_name := old_name)
}
df %>% mutate(data = map(data, f))
2) 在试图用 !!
强制计算的表达式外定义 new_name
new_name = sym("new_value")
df %>%
mutate(data = map(data, function(d) {d %>% rename(!!new_name := old_name)}))
3) 重写您的表达式,使其不使用 !!
运算符来评估尚未初始化的变量(在本例中为 new_name
):
df %>%
mutate(data = map(data, function(d) {
new_name = "new_value"
do.call( partial(rename, d), set_names(syms("old_name"), new_name) )
}))
旁注:您会注意到我用 sym()
替换了您的 quo()
调用。函数 quo()
捕获表达式及其环境。由于字符串文字 "new_value"
将始终评估为相同的值,因此无需标记其环境。通常,将列名捕获为符号的正确动词是 sym()
.
下面是一个简单示例,说明如何使用引号动态重命名 tibble 列。
quoteExample = function() {
new_name = quo("new_name_value");
tibble(old_name=list(1,2,3)) %>%
rename( !! quo_name(new_name) := old_name)
}
quoteExample()
结果= tibble(new_name_value=list(1,2,3))
除了这次在 lamda 中,下面是同一个简单示例。
{function ()
new_name = quo("new_name_value");
tibble(old_name=list(1,2,3)) %>%
rename( !! quo_name(new_name) := old_name)
} ()
结果= is_quosure 中的错误(现状):未找到对象 'new_name'
为什么引号在 lamda 中失败但在命名函数中却没有?这种差异从何而来?难道我做错了什么?
编辑:上面的例子已经被 Akrun 解决了,但是下面是另一个例子,尽管已经应用了建议的解决方案,但还是失败了:
df = tibble(data=list(tibble(old_name= c(1,2,3))))
df %>%
mutate(data = map(data, (function(d){
new_name = quo("new_value")
d %>% rename( !! quo_name(new_name) := old_name)
})))
结果:is_quosure 中的错误(现状):未找到对象 'new_name'
失败是因为另一个问题吗?
如果我们用 ()
或 {}
使其独立,它应该可以工作
(function() {
new_name = quo("new_name_value");
tibble(old_name=list(1,2,3)) %>%
rename( !! quo_name(new_name) := old_name)
})()
# A tibble: 3 x 1
# new_name_value
# <list>
#1 <dbl [1]>
#2 <dbl [1]>
#3 <dbl [1]>
如果匿名函数只包含一个expr
ession,则不需要使用{}
,但如果表达式多一行,则用{}
换行.根据?body
The bodies of all but the simplest are braced expressions, that is calls to {: see the ‘Examples’ section for how to create such a call.
这与 !!
运算符强制立即评估其参数, 在 创建匿名函数环境之前。在您的情况下, !! quo_name(new_name)
尝试找到 new_name
相对于整个表达式(即整个 mutate(...)
表达式)的定义。由于 new_name
是在表达式本身中定义的,因此您最终会遇到导致 "object not found" 错误的循环依赖。
你的三个选择是
1) 将您的 lambda 拉出到一个独立的函数中,以确保首先创建它的环境,从而在 !!
运算符强制其评估之前正确初始化该环境中的所有变量:
f <- function(d) {
new_name = sym("new_value")
d %>% rename(!!new_name := old_name)
}
df %>% mutate(data = map(data, f))
2) 在试图用 !!
new_name
new_name = sym("new_value")
df %>%
mutate(data = map(data, function(d) {d %>% rename(!!new_name := old_name)}))
3) 重写您的表达式,使其不使用 !!
运算符来评估尚未初始化的变量(在本例中为 new_name
):
df %>%
mutate(data = map(data, function(d) {
new_name = "new_value"
do.call( partial(rename, d), set_names(syms("old_name"), new_name) )
}))
旁注:您会注意到我用 sym()
替换了您的 quo()
调用。函数 quo()
捕获表达式及其环境。由于字符串文字 "new_value"
将始终评估为相同的值,因此无需标记其环境。通常,将列名捕获为符号的正确动词是 sym()
.