如何将字符串向量(每个都给出命令)转换为 R 中的函数?

How do I turn a vector of character strings (each giving commands) into a function in R?

我正在尝试构建一个函数,该函数从其输入创建并输出一个新的 函数(为方便起见,我将其称为输出函数)。我能够为输出函数生成我想要的代码行作为字符串,并将其放入一个字符向量中,该向量通过函数主体中我想要的命令。但是,我无法将其转换为输出函数。

我观察到我可以使用 body 命令提取函数的主体,并将其转换为字符向量。例如,以下是将此应用于 matrix 函数时得到的结果:

body(matrix)

{
    if (is.object(data) || !is.atomic(data)) 
        data <- as.vector(data)
    .Internal(matrix(data, nrow, ncol, byrow, dimnames, missing(nrow), 
        missing(ncol)))
}

COMMANDS <- as.character(body(matrix))
COMMANDS

[1] "{"                                                                                 
[2] "if (is.object(data) || !is.atomic(data)) data <- as.vector(data)"                  
[3] ".Internal(matrix(data, nrow, ncol, byrow, dimnames, missing(nrow), missing(ncol)))"

但是,我不知道如何逆向这个过程——也就是说,如果我构建了一个包含我的命令的字符向量,我该如何创建一个以这组命令为主体的函数?

可以使用 as.function 从列表创建函数。该列表必须包含一个类型为 "language" 的未命名元素,它充当函数的主体,列表中的任何命名项都被解释为函数的参数。

要从字符串创建函数体,我们可以使用 str2lang.

将字符串转换为语言对象

这意味着我们可以很容易地编写一个函数,从这样的字符串创建函数:

make_func <- function(..., body) {
  as.function(c(..., str2lang(body)))
}

所以,例如:

hello <- make_func(body = "print('hello world')")

hello()
#> [1] "hello world"

或者,如果我们的函数需要参数:

add_one <- make_func(x = 1, body = "{ x <- x + 1; return(x) }")

add_one(22)
#> [1] 23

正如其他人在评论中指出的那样,很少需要执行转换为字符的步骤,因为我们可以直接使用语言对象。

例如,要重新创建 matrix,我们可以这样做:

my_matrix <- as.function(c(formals(matrix), body(matrix)))
my_matrix(1:9, ncol = 3)
#>      [,1] [,2] [,3]
#> [1,]    1    4    7
#> [2,]    2    5    8
#> [3,]    3    6    9

如果出于某种原因,您有一个表示多行代码的字符向量,您可以简单地 paste 将它们与换行符一起使用。请注意,您需要确保结果用花括号括起来,以便 str2lang 对其进行解析。所以在你的例子中,我们可以这样做:

my_mat <- make_func(data = NA, ncol = 1, nrow = 1, byrow = FALSE, 
                    dimnames = list(NULL),
                    body = paste0(paste(code_lines, collapse = '\n'), '}'))

my_mat(1:9, ncol = 3)
#>      [,1] [,2] [,3]
#> [1,]    1    4    7
#> [2,]    2    5    8
#> [3,]    3    6    9

为了完整起见,我将添加另一个答案,将所需的 material 合并为一个函数 generate_function 以生成输出函数。该函数采用一组参数和一个(字符)命令向量,并生成实现这些命令的 output-function。

generate_function <- function(..., commands, envir = parent.frame(), 
                              openbracket = TRUE, closebracket = FALSE) { 
  body <- paste(commands, collapse = '\n')
  if (!openbracket)  { body <- paste(c('{', body), collapse = '\n') }
  if (!closebracket) { body <- paste(c(body, '}'), collapse = '\n') }
  as.function(c(..., str2lang(body)), envir = envir) }

我们可以应用此函数来恢复问题中用作示例的matrix函数。为此,我们列出了与函数中相同的参数,并将其命令用作输入。

COMMANDS <- as.character(body(matrix))
FUNCTION <- generate_function(data = NA, nrow = 1, ncol = 1, byrow = FALSE, 
                              dimnames = list(NULL), commands = COMMANDS)

我们现在可以确认新创建函数的形式和主体与 matrix 函数匹配。 (函数的环境不同,因为新函数存在于我们正在工作的父环境中,正如我们所希望的那样。)

identical(body(FUNCTION), body(matrix))
[1] TRUE
identical(formals(FUNCTION), formals(matrix))
[1] TRUE