Я пытаюсь создать «реактивную и запоминающуюся» версию существующей функции, используя функции из shiny
- но для использования вне блестящего контекста.
Круто полагаться на shiny::reactive()
это то, что
- он «автоматически» знает о своих реактивных зависимостях
- обрабатывает решение «вернуть кеш или повторно выполнить основное выражение» для нас
Хотя мой подход к передаче тела функции (вместо самой функции ) сам по себе 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
Создано в 2019-01-23 пакетом Представить (v0.2.1)
Подход 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
Создан в 2019-01-23 пакетом Представить (v0.2.1)
Обратите внимание, что при передаче тела от 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
Создано в 2019-01-23 пакетом Представ (v0.2.1)
Странно, попытка использовать missing()
и match.arg()
в подходе 1 не приводит к ошибкам при запуске кода через reprex::reprex()
, но это происходит в интерактивном режиме.