y
не "ушел навсегда", потому что канал вызывает вашу функцию, и он также знает о y
. Есть способ восстановить y
, но он требует некоторого обхода стека вызовов. Чтобы понять, что происходит, мы будем использовать ?sys.frames
и ?sys.calls
:
«Sys.calls» и «sys.frames» дают список всех активных вызовов и кадров, соответственно, а «sys.parents» возвращает целочисленный вектор индексов родительских кадров каждого из этих кадров.
Если мы рассыпаем их по всему вашему 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
, находится в родительской среде функции (второй снизу стека), в то время как вызов канала, который знает о y
, находится на самом верху стека.
Есть несколько способов пройти по стеку. ответ @ MrFlick на аналогичный вопрос, а также эта проблема GitHub пересекает фреймы / среды из sys.frames()
. Здесь я покажу альтернативу, которая просматривает sys.calls()
и анализирует выражения, чтобы найти %>%
.
Первая часть головоломки - определить функцию, которая преобразует выражение в его Абстрактное дерево Sytax (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, где первым элементом является %>%
; второй элемент будет соответствовать левой стороне трубы (symbol a
в a %>% b
примере). Если таких 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()
возвращает выражение, а не запрос, мы хотим сделать то же самое и в случае по умолчанию.)
Новая функция довольно надежна и работает внутри других функций, включая вложенные %>%
конвейеры:
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
person
Artem Sokolov
schedule
29.08.2018
rlang
, так что я надеюсь, что у кого-то получится здесь лучше. - person Rory Nolan   schedule 29.08.2018