r - R purrr::partial -- 它如何处理部分化的参数?
问题描述
很长一段时间以来,我一直是 Rpurrr
包的热心用户,最近遇到了一个关于purrr::partial
. 假设我定义了一个有两个参数的函数
f <- function(x, y) x + y
并通过将y
参数设置为某个全局变量的值来部分化它:
yy <- 1
fp <- partial(f, y = !!yy)
fp(3) # 3 + 1 = 4
取消引用yy
(即使用y = !!yy
而不是y = yy
)导致yy
仅在创建时评估一次fp
;特别是,yy
在此步骤之后进行修改不会改变fp
:
yy <- 2
fp(3) # still: 3 + 1 = 4
partial
这是我的问题:评估后到底做了什么yy
?——我看到了两种可能性:
- 的值
yy
“硬连线”到 的主体中fp
,这意味着它在被调用时不会作为参数传递fp
。 - 的值
yy
或多或少被视为y
参数的默认值(没有覆盖默认值的选项),这意味着fp
内部调用f
(或它的副本),其值yy
作为与匹配的参数静默传递给y
. 在这种情况下fp
,它只不过是围绕f
.
试图探索第二种可能性,我修改了f
after defined 的定义fp
。这不改变fp
,意思是fp
不包含任何外部引用f
;但是,这并不排除fp
包含旧版本的副本的(理论上)可能性f
。(结论:这种方法没有帮助。)
激发我的问题的一些实际背景:在我当前的项目中,我定义了许多函数,这些函数使用(a)因调用而异的参数,(b)表示“配置数据”或“领域知识”的参数。与 (b) 参数匹配的数据(可能是相当多的数据)不会因调用而改变,但可能会在我提交更新时改变;无论如何,我相信这些数据不应该在我的函数中硬编码。我的策略是在启动时从一些文件中读取配置数据,并通过部分化 (b) 中的参数将其集成到我的函数中。通过应用偏函数purrr::pmap
to some tibbles 结果有点慢,这让我怀疑在调用函数时配置数据可能仍会传递——因此我的问题。(如果有人对上面简要描述的“局部化策略”有一些想法,我也会对这些产生浓厚的兴趣。)
解决方案
似乎是选项2。尝试:
f <- function(x, y) x + y
yy <- 5
fp1 <- partial(f, y = !! yy)
debugonce(f)
fp1(3)
在这里您可以看到,如果在 RStudio 中,调试器将打开f
参数x = 3
和y = 5
传递给的原始函数。但是,偏函数不是调用真正的函数f
,而是调用它的引用副本。如果您f
在它被部分化后进行更改,调试器将不再找到它。
f <- function(x, y) x + y
yy <- 5
fp1 <- partial(f, y = !! yy)
f <- function(x, y) x + 2 * y
debugonce(f)
fp1(3) # debugger will not open
partial
可以通过构造函数来对自己进行局部化来模仿 的行为。但是,在这种情况下,既f
不会也不会yy
被捕获,因此更改它们会影响您的偏函数的输出:
f <- function(x, y) x + y
yy <- 5
# similar to `partial` but captures neither `f` nor `yy`
fp2 <- function(x) f(x, yy)
fp2(3)
#> [1] 8
# so if yy changes, so will the output of fp2
yy <- 10
fp2(3)
#> [1] 13
# and if f changes, so will the output of fp2
f <- function(x, y) x + 2 * y
fp2(3)
#> [1] 23
由reprex 包(v0.3.0)于 2020 年 7 月 13 日创建
为了更好地理解它是如何工作的,我们可以通过以下方式partial
构造一个函数:simple_partial
library(rlang)
f <- function(x, y) x + y
yy <- 5
simple_partial <- function(.f, ...) {
# capture arguments
args <- enquos(...)
# capture function
fn_expr <- enexpr(.f)
# construct call with function and supplied arguments
# in the ... go all arguments which will be supplied later
call <- call_modify(call2(.f), !!! args, ... = )
# turn call into a quosure (= expr and environment where it should be evaluated)
call <- new_quosure(call, caller_env())
# create child environment of current environment and turn it into a data mask
mask <- new_data_mask(env())
# return this function
function(...) {
# bind the ... from current environment to the data mask
env_bind(mask, ... = env_get(current_env(), "..."))
# evaluate the quoted call in the data mask where all additional values can be found
eval_tidy(call, mask)
}
}
fp3 <- simple_partial(f, y = !! yy)
fp3(1)
#> [1] 6
由reprex 包(v0.3.0)于 2020 年 7 月 13 日创建
推荐阅读
- javascript - 在 JavaScript 中将日期转换为另一个时区并使用正确的时区打印
- python-3.x - BS4 找不到 li 项?
- swift - 请求结果结构 firebasedatabase Swift
- python - 无法在 conda python 3.8 env 上正确安装 awswrangler(连接问题)
- php - Wordpress 永久链接 Apache mod_rewrite 启用但仍然无法正常工作
- tensorflow - 关于 NN 中的输入
- python - 从 pandas 数据框列中的列表创建配对嵌套列表,其中第一对的结束元素应该是下一个的开始元素
- linux-kernel - 尝试安装 VirtualBox-6.1 时出错(安全启动开启)
- class - 这是使用引导程序 4 将活动类添加到导航栏的最有效方法吗
- python - 将 python 字节传递给 Presto 查询的正确方法(并检索和解码这些字节)