获取在 `magrittr` 管道调用的函数中计算为点的表达式
Get expression that evaluated to dot in function called by `magrittr` pipe
我有一个函数 x_expression()
,它打印传递给参数 x
的表达式。
pacman::p_load(magrittr, rlang)
x_expression <- function(x) {
print(enquo(x))
}
y <- 1
x_expression(y)
#> <quosure>
#> expr: ^y
#> env: global
y %>% x_expression()
#> <quosure>
#> expr: ^.
#> env: 0x7ff27c36a610
所以你可以看到它知道 y
被传递给它,但是当 y
通过 %>%
传入时,函数 returns 打印 .
.有没有办法恢复 y
如果它被管道输入,或者它永远消失了?简而言之,我想要的是一个类似于 x_expression()
的函数,但在上述两种情况下都会打印 y
的函数。
这个问题确实和类似,但是稍微笼统一些。这个人只要数据框的名字,我要表达式,不管是什么。但是,相同的答案可能适用于两者。我不喜欢这个几乎重复的问题的答案,也不喜欢那个答案的作者。
y
不是 "gone forever",因为管道调用你的函数,它也知道 y
。有一种方法可以恢复 y
,但它需要对调用堆栈进行一些遍历。要了解发生了什么,我们将使用 ?sys.frames
和 ?sys.calls
:
‘sys.calls’ and ‘sys.frames’ give a pairlist of all the active calls and frames, respectively, and ‘sys.parents’ returns an integer vector of indices of the parent frames of each of those frames.
如果我们将这些散布在您的 x_expression()
中,我们可以看到当我们从全局环境中调用 y %>% x_expression()
时会发生什么:
x_expression <- function(x) {
print( enquo(x) )
# <quosure>
# expr: ^.
# env: 0x55c03f142828 <---
str(sys.frames())
# Dotted pair list of 9
# $ :<environment: 0x55c03f151fa0>
# $ :<environment: 0x55c03f142010>
# ...
# $ :<environment: 0x55c03f142828> <---
# $ :<environment: 0x55c03f142940>
str(sys.calls())
# Dotted pair list of 9
# $ : language y %>% x_expression() <---
# $ : language withVisible(eval(...
# ...
# $ : language function_list[[k]...
# $ : language x_expression(.)
}
我用 <---
突出显示了重要部分。请注意,enquo
捕获的 quosure 存在于函数的父环境中(堆栈底部的第二个),而知道 y
的管道调用一直位于函数的顶部堆叠。
有几种遍历堆栈的方法。 to a similar question as well as this GitHub issue 遍历来自 sys.frames()
的帧/环境。在这里,我将展示一个遍历 sys.calls()
并解析表达式以找到 %>%
.
的替代方案
第一个难题是定义一个函数,将表达式转换为其 Abstract Sytax Tree(AST):
# Recursively constructs Abstract Syntax Tree for a given expression
getAST <- function(ee) purrr::map_if(as.list(ee), is.call, getAST)
# Example: getAST( quote(a %>% b) )
# List of 3
# $ : symbol %>%
# $ : symbol a
# $ : symbol b
我们现在可以系统地将此函数应用于整个 sys.calls()
堆栈。目标是识别第一个元素为 %>%
的 AST;第二个元素将对应于管道的左侧(a %>% b
示例中的 symbol a
)。如果有多个这样的 AST,那么我们就处于嵌套 %>%
管道场景中。在这种情况下,列表中的最后一个 AST 将在调用堆栈中处于最低位置并且最接近我们的函数。
x_expression2 <- function(x) {
sc <- sys.calls()
ASTs <- purrr::map( as.list(sc), getAST ) %>%
purrr::keep( ~identical(.[[1]], quote(`%>%`)) ) # Match first element to %>%
if( length(ASTs) == 0 ) return( enexpr(x) ) # Not in a pipe
dplyr::last( ASTs )[[2]] # Second element is the left-hand side
}
(小注:我使用 enexpr()
而不是 enquo()
来确保函数在管道内外的行为一致。由于 sys.calls()
遍历 returns 一个表达式,而不是 quosure,我们也想在默认情况下做同样的事情。)
新函数非常强大,可以在其他函数中运行,包括嵌套的 %>%
管道:
x_expression2(y)
# y
y %>% x_expression2()
# y
f <- function() {x_expression2(v)}
f()
# v
g <- function() {u <- 1; u %>% x_expression2()}
g()
# u
y %>% (function(z) {w <- 1; w %>% x_expression2()}) # Note the nested pipes
# w
我有一个函数 x_expression()
,它打印传递给参数 x
的表达式。
pacman::p_load(magrittr, rlang)
x_expression <- function(x) {
print(enquo(x))
}
y <- 1
x_expression(y)
#> <quosure>
#> expr: ^y
#> env: global
y %>% x_expression()
#> <quosure>
#> expr: ^.
#> env: 0x7ff27c36a610
所以你可以看到它知道 y
被传递给它,但是当 y
通过 %>%
传入时,函数 returns 打印 .
.有没有办法恢复 y
如果它被管道输入,或者它永远消失了?简而言之,我想要的是一个类似于 x_expression()
的函数,但在上述两种情况下都会打印 y
的函数。
这个问题确实和
y
不是 "gone forever",因为管道调用你的函数,它也知道 y
。有一种方法可以恢复 y
,但它需要对调用堆栈进行一些遍历。要了解发生了什么,我们将使用 ?sys.frames
和 ?sys.calls
:
‘sys.calls’ and ‘sys.frames’ give a pairlist of all the active calls and frames, respectively, and ‘sys.parents’ returns an integer vector of indices of the parent frames of each of those frames.
如果我们将这些散布在您的 x_expression()
中,我们可以看到当我们从全局环境中调用 y %>% x_expression()
时会发生什么:
x_expression <- function(x) {
print( enquo(x) )
# <quosure>
# expr: ^.
# env: 0x55c03f142828 <---
str(sys.frames())
# Dotted pair list of 9
# $ :<environment: 0x55c03f151fa0>
# $ :<environment: 0x55c03f142010>
# ...
# $ :<environment: 0x55c03f142828> <---
# $ :<environment: 0x55c03f142940>
str(sys.calls())
# Dotted pair list of 9
# $ : language y %>% x_expression() <---
# $ : language withVisible(eval(...
# ...
# $ : language function_list[[k]...
# $ : language x_expression(.)
}
我用 <---
突出显示了重要部分。请注意,enquo
捕获的 quosure 存在于函数的父环境中(堆栈底部的第二个),而知道 y
的管道调用一直位于函数的顶部堆叠。
有几种遍历堆栈的方法。 sys.frames()
的帧/环境。在这里,我将展示一个遍历 sys.calls()
并解析表达式以找到 %>%
.
第一个难题是定义一个函数,将表达式转换为其 Abstract Sytax Tree(AST):
# Recursively constructs Abstract Syntax Tree for a given expression
getAST <- function(ee) purrr::map_if(as.list(ee), is.call, getAST)
# Example: getAST( quote(a %>% b) )
# List of 3
# $ : symbol %>%
# $ : symbol a
# $ : symbol b
我们现在可以系统地将此函数应用于整个 sys.calls()
堆栈。目标是识别第一个元素为 %>%
的 AST;第二个元素将对应于管道的左侧(a %>% b
示例中的 symbol a
)。如果有多个这样的 AST,那么我们就处于嵌套 %>%
管道场景中。在这种情况下,列表中的最后一个 AST 将在调用堆栈中处于最低位置并且最接近我们的函数。
x_expression2 <- function(x) {
sc <- sys.calls()
ASTs <- purrr::map( as.list(sc), getAST ) %>%
purrr::keep( ~identical(.[[1]], quote(`%>%`)) ) # Match first element to %>%
if( length(ASTs) == 0 ) return( enexpr(x) ) # Not in a pipe
dplyr::last( ASTs )[[2]] # Second element is the left-hand side
}
(小注:我使用 enexpr()
而不是 enquo()
来确保函数在管道内外的行为一致。由于 sys.calls()
遍历 returns 一个表达式,而不是 quosure,我们也想在默认情况下做同样的事情。)
新函数非常强大,可以在其他函数中运行,包括嵌套的 %>%
管道:
x_expression2(y)
# y
y %>% x_expression2()
# y
f <- function() {x_expression2(v)}
f()
# v
g <- function() {u <- 1; u %>% x_expression2()}
g()
# u
y %>% (function(z) {w <- 1; w %>% x_expression2()}) # Note the nested pipes
# w