在闪亮的上下文之外创建一个反应式和可记忆的功能
Creating a reactive and memoizable function outside of shiny context
我正在尝试通过利用 shiny
中的函数来创建现有函数的“反应式和可记忆”版本 - 但要在闪亮的上下文之外使用。
依靠 shiny::reactive()
的好处在于
- 它“自动”知道它的反应依赖性
- 它为我们处理“return 缓存或重新执行底层表达式”的决定
虽然我将函数的主体(而不是函数本身)传递给shiny::reactive()
的方法本身有效,它让我放弃了仅适用于 missing()
或 match.arg()
.
等功能的东西
但我似乎无法找到一种方法将函数本身交给 shiny::reactive()
,同时仍然使用它的内置 caching/memoization。要看到这一点,请注意 foo()
实际上在我们调用 foo_react()
时 每次 执行,因此在第二种方法中没有缓存起作用
方法一
# Preliminaries -----
library(shiny)
library(rlang)
options(shiny.suppressMissingContextError=TRUE)
shiny:::setAutoflush(TRUE)
makeReactiveBinding("x_react")
makeReactiveBinding("foo")
# Approach 1: hand foo()'s body to reactive() ----
foo <- function(x_react = get("x_react", 1)) {
message("Executing foo()")
x_react * 10
}
expr_inner <- quo(!!fn_body(foo))
expr_react <- quo(reactive({!!expr_inner}))
foo_react <- eval_tidy(expr_react)
print(foo_react)
#> reactive({
#> ~{
#> message("Executing foo()")
#> x_react * 10
#> }
#> })
x_react <- 1
foo_react() # Executes body of foo()
#> Executing foo()
#> [1] 10
foo_react() # Uses cached result
#> [1] 10
x_react <- 10
foo_react() # Executes body of foo()
#> Executing foo()
#> [1] 100
foo_react() # Uses cached result
#> [1] 100
由 reprex package (v0.2.1)
于 2019-01-23 创建
方法二
# Approach 2: handing foo() itself to reactive() -----
expr_inner <- quo(!!foo)
expr_react <- quo(shiny::reactive({!!expr_inner}))
foo_react <- eval_tidy(expr_react)
print(foo_react)
#> reactive({
#> ~function (x_react = get("x_react", 1))
#> {
#> message("Executing foo()")
#> x_react * 10
#> }
#> })
x_react <- 1
foo_react()() # Executes foo()
#> Executing foo()
#> [1] 10
foo_react()() # Does NOT use cached result, but executes foo() again
#> Executing foo()
#> [1] 10
x_react <- 10
foo_react()() # Executes foo()
#> Executing foo()
#> [1] 100
foo_react()() # Does NOT use cached result, but executes foo() again
#> Executing foo()
#> [1] 100
由 reprex package (v0.2.1)
于 2019-01-23 创建
请注意,当将 foo()
的主体交给 reactive()
时,我们将失去使用 missing()
或 match.arg()
之类的东西的能力
foo <- function(x_react = get("x_react", 1), y = c("a", "b")) {
message("Executing foo()")
try(print(missing(x)))
try(print(match.arg(y)))
x_react * 10
}
# Approach 1 -----
expr_inner <- quo(!!fn_body(foo))
expr_react <- quo(reactive({!!expr_inner}))
foo_react <- eval_tidy(expr_react)
x_react <- 1
foo_react() # Executes body of foo()
#> Executing foo()
#> [1] 10
# Approach 2 -----
expr_inner <- quo(!!foo)
expr_react <- quo(shiny::reactive({!!expr_inner}))
foo_react <- eval_tidy(expr_react)
x_react <- 1
foo_react()() # Executes foo()
#> Executing foo()
#> [1] TRUE
#> [1] "a"
#> [1] 10
由 reprex package (v0.2.1)
于 2019-01-23 创建
奇怪的是,尝试在方法 1 中使用 missing()
和 match.arg()
不会导致错误,当 运行 代码通过 reprex::reprex()
时,但它会在交互模式。
抱歉,我不太了解 rlang 的内容。但是你能不能只在反应式表达式中调用 foo()
函数,将它包装在一个函数中以在需要时传递参数?我试过像这样调整方法 2:
library(shiny)
library(rlang)
options(shiny.suppressMissingContextError=TRUE)
shiny:::setAutoflush(TRUE)
makeReactiveBinding("x_react")
makeReactiveBinding("foo")
foo <- function(x_react = get("x_react", 1), y = c("a", "b")) {
message("Executing foo()")
try(print(missing(x_react)))
try(print(match.arg(y)))
x_react * 10
}
foo_react <- function(...) {
reactive({
foo(...)
})
}
## no args
f <- foo_react()
x_react <- 1
f()
# Executing foo()
# [1] TRUE
# [1] "a"
# [1] 10
f()
# [1] 10
x_react <- 10
f()
# Executing foo()
# [1] TRUE
# [1] "a"
# [1] 100
f()
# [1] 100
## with args
f <- foo_react(x_react = 3, y = "b")
f()
# Executing foo()
# [1] FALSE
# [1] "b"
# [1] 30
f()
# [1] 30
我正在尝试通过利用 shiny
中的函数来创建现有函数的“反应式和可记忆”版本 - 但要在闪亮的上下文之外使用。
依靠 shiny::reactive()
的好处在于
- 它“自动”知道它的反应依赖性
- 它为我们处理“return 缓存或重新执行底层表达式”的决定
虽然我将函数的主体(而不是函数本身)传递给shiny::reactive()
的方法本身有效,它让我放弃了仅适用于 missing()
或 match.arg()
.
但我似乎无法找到一种方法将函数本身交给 shiny::reactive()
,同时仍然使用它的内置 caching/memoization。要看到这一点,请注意 foo()
实际上在我们调用 foo_react()
时 每次 执行,因此在第二种方法中没有缓存起作用
方法一
# Preliminaries -----
library(shiny)
library(rlang)
options(shiny.suppressMissingContextError=TRUE)
shiny:::setAutoflush(TRUE)
makeReactiveBinding("x_react")
makeReactiveBinding("foo")
# Approach 1: hand foo()'s body to reactive() ----
foo <- function(x_react = get("x_react", 1)) {
message("Executing foo()")
x_react * 10
}
expr_inner <- quo(!!fn_body(foo))
expr_react <- quo(reactive({!!expr_inner}))
foo_react <- eval_tidy(expr_react)
print(foo_react)
#> reactive({
#> ~{
#> message("Executing foo()")
#> x_react * 10
#> }
#> })
x_react <- 1
foo_react() # Executes body of foo()
#> Executing foo()
#> [1] 10
foo_react() # Uses cached result
#> [1] 10
x_react <- 10
foo_react() # Executes body of foo()
#> Executing foo()
#> [1] 100
foo_react() # Uses cached result
#> [1] 100
由 reprex package (v0.2.1)
于 2019-01-23 创建方法二
# Approach 2: handing foo() itself to reactive() -----
expr_inner <- quo(!!foo)
expr_react <- quo(shiny::reactive({!!expr_inner}))
foo_react <- eval_tidy(expr_react)
print(foo_react)
#> reactive({
#> ~function (x_react = get("x_react", 1))
#> {
#> message("Executing foo()")
#> x_react * 10
#> }
#> })
x_react <- 1
foo_react()() # Executes foo()
#> Executing foo()
#> [1] 10
foo_react()() # Does NOT use cached result, but executes foo() again
#> Executing foo()
#> [1] 10
x_react <- 10
foo_react()() # Executes foo()
#> Executing foo()
#> [1] 100
foo_react()() # Does NOT use cached result, but executes foo() again
#> Executing foo()
#> [1] 100
由 reprex package (v0.2.1)
于 2019-01-23 创建请注意,当将 foo()
的主体交给 reactive()
时,我们将失去使用 missing()
或 match.arg()
foo <- function(x_react = get("x_react", 1), y = c("a", "b")) {
message("Executing foo()")
try(print(missing(x)))
try(print(match.arg(y)))
x_react * 10
}
# Approach 1 -----
expr_inner <- quo(!!fn_body(foo))
expr_react <- quo(reactive({!!expr_inner}))
foo_react <- eval_tidy(expr_react)
x_react <- 1
foo_react() # Executes body of foo()
#> Executing foo()
#> [1] 10
# Approach 2 -----
expr_inner <- quo(!!foo)
expr_react <- quo(shiny::reactive({!!expr_inner}))
foo_react <- eval_tidy(expr_react)
x_react <- 1
foo_react()() # Executes foo()
#> Executing foo()
#> [1] TRUE
#> [1] "a"
#> [1] 10
由 reprex package (v0.2.1)
于 2019-01-23 创建奇怪的是,尝试在方法 1 中使用 missing()
和 match.arg()
不会导致错误,当 运行 代码通过 reprex::reprex()
时,但它会在交互模式。
抱歉,我不太了解 rlang 的内容。但是你能不能只在反应式表达式中调用 foo()
函数,将它包装在一个函数中以在需要时传递参数?我试过像这样调整方法 2:
library(shiny)
library(rlang)
options(shiny.suppressMissingContextError=TRUE)
shiny:::setAutoflush(TRUE)
makeReactiveBinding("x_react")
makeReactiveBinding("foo")
foo <- function(x_react = get("x_react", 1), y = c("a", "b")) {
message("Executing foo()")
try(print(missing(x_react)))
try(print(match.arg(y)))
x_react * 10
}
foo_react <- function(...) {
reactive({
foo(...)
})
}
## no args
f <- foo_react()
x_react <- 1
f()
# Executing foo()
# [1] TRUE
# [1] "a"
# [1] 10
f()
# [1] 10
x_react <- 10
f()
# Executing foo()
# [1] TRUE
# [1] "a"
# [1] 100
f()
# [1] 100
## with args
f <- foo_react(x_react = 3, y = "b")
f()
# Executing foo()
# [1] FALSE
# [1] "b"
# [1] 30
f()
# [1] 30