7

假设我有一个闭包add_y(y),它返回一个添加y到其输入的函数。

add_y <- function(y) {
  function(x) {
    x + y
  }
}
add_4 <- add_y(4)

所以 的值add_4是一个将 4 添加到其输入的函数。这行得通。我想dput用来显示 as 的add_4定义

function(x) {
  x + 4
}

但这不是 dput 返回的。

add_y <- function(y) {
  function(x) {
    x + y
  }
}
add_4 <- add_y(4)
dput(add_4)
#> function (x) 
#> {
#>     x + y
#> }

有没有办法获得可以在封闭环境之外运行的源代码?

4

4 回答 4

6

如果您控制 add_y ,则解决方法是将 y 的值直接注入内部函数的主体,或者将其注入形式参数列表。这消除了环境的使用,因此问题不再存在。这涉及命名匿名内部函数,并且只需要一行额外的代码来执行注入,再加上一行来返回结果。

# 1. inject into body
add_y2 <- function(y) {
  inner <- function(x) {
    x + y
  }
  body(inner) <- do.call("substitute", list(body(inner)))
  inner
}
# test
add_4 <- add_y2(4)
dput(add_4)
## function (x) 
## {
##     x + 4
## }

# 2. inject into formal arguments
add_y3 <- function(y) {
  inner <- function(x) {
    x + y
  }
  formals(inner)$y <- y
  inner
}
# test
add_4 <- add_y3(4)
dput(add_4)
## function (x, y = 4) 
## {
##     x + y
## }
于 2021-12-24T21:42:30.310 回答
3

这可以工作,但它涉及更改add_y.

library(rlang)
library(magrittr)
library(stringr)

add_y <- function(y) {
  fn <- expr(function(x) {
    x+!!y
  })
  fn <- deparse(fn) %>% str_c(collapse = "")
  fn <- eval(parse(text = fn))
}

add_4 <- add_y(4)

dput(add_4)
#> function (x) 
#> {
#>     x + 4
#> }

reprex 包于 2021-12-24 创建(v2.0.1)

于 2021-12-24T20:27:37.453 回答
2

您可以构造一个dput替换来生成创建类似函数的代码add_4,但它不会按照您想要的方式进行解析:

dput_with_env <- function(f) {
  fn <- deparse(f, control = c("keepNA", "keepInteger", 
                               "niceNames", "showAttributes"))
  env <- as.list(environment(f))
  cat("local({ f =\n")
  cat(fn, sep = "\n")
  cat("\nenvironment(f) <- list2env(\n")
  dput(env)
  cat(")\nf})")
}

add_y <- function(y) {
  function(x) {
    x + y
  }
}
add_4 <- add_y(4)

dput_with_env(add_4)
#> local({ f =
#> function (x) 
#> {
#>     x + y
#> }
#> 
#> environment(f) <- list2env(
#> list(y = 4)
#> )
#> f})

reprex 包于 2021-12-24 创建(v2.0.1)

这假设环境add_4非常简单,因此其环境的父级可以是您评估代码时所在的环境。我们可以试一试:

newfn <- local({ f =
function (x) 
{
   x + y
}
environment(f) <- list2env(
list(y = 4)
)
f})

newfn
#> function (x) 
#> {
#>    x + y
#> }
#> <environment: 0x7f9a1b5e2318>
newfn(1)
#> [1] 5

reprex 包于 2021-12-24 创建(v2.0.1)

于 2021-12-24T20:51:42.493 回答
1

没有dput(),没有。该dput()函数不会创建环境的文本表示。

如果你想保存函数,你可以这样做

save(add_4, file="add4.Rdata")

然后在另一个 R 会话中

load("add4.Rdata")

这将捕获所有包含的值,并且您的函数将像以前一样运行

于 2021-12-24T19:56:05.490 回答