在闪亮的上下文之外创建反应式和可记忆的功能

时间:2019-01-23 10:40:42

标签: r shiny memoization reactive

我正在尝试通过利用shiny中的函数来创建现有函数的“反应性和可记忆性”版本-但要在闪亮的环境之外使用。

依赖shiny::reactive()的有趣之处在于

  1. “自动”了解其反应依赖性
  2. 它为我们处理“返回缓存或重新执行基础表达”的决定

虽然我将函数的 body (而不是函数自身)交给shiny::reactive()的方法本身是可行的,但这使我放弃了仅适用于missing()match.arg()之类的功能。

但是我似乎找不到找到将函数本身传递给shiny::reactive()的方法,同时仍然利用其内置的缓存/内存化功能。要看到这一点,请注意foo()实际上是在每次我们调用foo_react()的时间执行的,因此在第二种方法中没有缓存起作用

方法1

# 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创建

方法2

# 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创建

奇怪的是,在通过missing()运行代码时,尝试在方法1中使用match.arg()reprex::reprex()不会导致错误,但是在交互模式下确实如此。

1 个答案:

答案 0 :(得分:1)

对不起,您对Rlang的了解还不够。但是您是否可以在反应式表达式中调用foo()函数,并在需要时将其包装在函数中以传递args?我尝试像这样调整方法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