繁体   English   中英

在新环境的 RMarkdown 中使用 map 中的胶水

[英]Use of glue in map in an RMarkdown in a new environment

考虑以下Rmarkdown文档:

---
title: "Environments"
author: "Me"
date: "2023-01-13"
output: html_document
---

```{r setup}
library(glue)
library(purrr)
```

```{r vars}
a <- 1
x <- list("`a` has the value: {a}")
```

```{r works}
glue(x[[1L]])
```

```{r does-not-work, error = TRUE}
map_chr(x, glue)
```

使用RStudio's knit button 时,一切都很顺利,output 如下所示:

正确呈现所有块的呈现的 HTML 文件

但是,如果我尝试使用自己的环境调用 render 自己,则会失败:

ne <- new.env()
render("env.Rmd", envir = ne)

呈现的 HTML 文件,其中第二个块产生错误

因此,当在purrr::map中使用时,显然glue会在环境中绊倒。

如何在不产生此错误的情况下使用自己的环境调用render 理想情况下,我不想更改Rmarkdown本身。

更新

有趣的是,如果我将glue包裹在自己的function中,事情就会再次顺利进行:

```
glue <- function(...) glue::glue(...)
map_chr(x, glue)
```

更新 2

这个问题似乎与knitr/rmarkdown ,而是一个一般的范围界定问题,似乎与定义所涉及功能的环境有关:

library(rlang)
library(purrr)
library(glue)
rm(list = ls())

e <- env(a = 1, x = "`a` has the value: {a}")
delayedAssign("res", map_chr(x, glue), e, e)
e$res
# Error:
# ℹ In index: 1.
# Caused by error:
# ! object 'a' not found

## as opposed to

a <- 1
x <- "`a` has the value: {a}"
delayedAssign("res", {
   map_chr(x, glue)
})
res

# [1] "`a` has the value: 1"

这与 RMarkdown 或“胶水”无关。 这也不是错误,这与我之前声称的相反。 事实上,这个问题可以通过简单地访问环境e中的一个变量来重现,例如通过get function:

e = env(a = 1)
local(map_chr("a", get), envir = e)
# Error in .f(.x[[i]], ...) : object 'a' not found

这是 R 的词法范围规则的结果:

lapply在其调用框架内执行FUN 1由于 R 作用域的工作方式, 2 FUN将在其调用 scope 中查找变量名称。此调用 scope 是lapply调用框架。 当然aFUN的调用框架中不存在(相比之下, XFUN存在,因为它们是lapply的参数名称)。

如果 R 在本地 scope 中没有找到名称,则继续“向上”查找,在当前环境的父环境中查找。 调用框架的父环境是定义function 的环境。 对于lapply ,这是namespace:base

namespace:base没有定义名称a ,因此继续向上搜索。 它的父环境是.GlobalEnv 3这就是为什么lapply("a", get)起作用的原因(纯属偶然!)如果我们在全局环境中定义a 然而,在我们的例子中,我们在另一个环境中定义a一个环境,这个环境永远不会被搜索,除非我们将它attach()到搜索路径(但这当然是一个坏主意)。

解决方法是在 function 中调用 function( glueget ,或任何需要访问局部变量的方法)。严格来说,我们应该始终这样做,而不仅仅是在不同的环境中工作时):

local(map_chr("a", \(.) get(.)), envir = e)
# [1] "1.000000"

这是有效的,因为匿名 function \(.) get(.)是在调用 scope 中定义的,在这个例子中,它是e 所以当lapply执行这个 function 时,它首先搜索它自己的 scope,没有a ,然后沿着父环境链向上走。 第一个父环境是定义匿名 function 的环境: e

但是请注意,我们需要注意参数名称的选择,因为匿名 function 的 scope 是第一个被搜索的:它具有优先权并且可以隐藏我们想要的变量:

e = env(a = 1, x = "`a` has the value: {a}")

# Works:
local(lapply(x, \(v) glue(v)), envir = e)
# [[1]]
# `a` has the value: 1

# Fails:
local(lapply(x, \(a) glue(a)), envir = e)
# [[1]]
# `a` has the value: `a` has the value: {a}

1实际上lapply在C中是作为内部function实现的; 但为了便于讨论,我们可以假设它在 R 中定义如下:

lapply = function (X, FUN, ...) {
  FUN = match.fun(FUN)
  if (!is.vector(X) || is.object(X)) X = as.list(X)
  res = vector('list', length(X))

  for (i in seq_along(X)) res[[i]] = FUN(X[[i]], ...)
  res
}

2我想强调的是,R 的范围规则非常合理并且在内部是一致的,即使在这种情况下不方便。 事实上,词法范围通常优于其他范围规则。

3我之前曾争论过,这实际上是R中的一个错误。 至少这是一个严重有问题的设计决策,会导致错误和误解,这个问题就是一个很好的例子。 出于这个原因,我的 package 'box'以不同的方式定义了模块环境, 特别是为了避免这种行为

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM