1

我想从一个函数在全局环境中创建一个 S3 泛型。我看了看R.methodsS3::setGenericS3.default并想出了以下内容:

create_generic <- function(nm) {
  src <- sprintf("%s <- function(obj, ...) UseMethod(\"%s\")", nm, nm)
  expr <- parse(text = src)
  print(expr)
  eval(expr, env = .GlobalEnv)
}

create_generic("cat")
#> expression(cat <- function(obj, ...) UseMethod("cat"))
cat
#> function (obj, ...) 
#> UseMethod("cat")

这适用于我想要的方式。但是,我一直在尝试使用引用来完成这项工作,但我被困住了:

library(rlang)

create_generic2 <- function(nm) {
  expr <- expr(!!sym(nm) <- function(obj, ...) UseMethod(!!nm))
  print(expr)
  eval(expr, env = .GlobalEnv)
}

create_generic2("dog")
#> dog <- function(obj, ...) UseMethod("dog")
dog
#> function(obj, ...) UseMethod(!!nm)

tidyeval因为这是我熟悉的,所以使用它,但我想看看它在基础 R 中是什么样子的。

我感兴趣的任何版本都可以在没有字符串操作的情况下工作create_generic

4

2 回答 2

2

在基础 R 中:

create_generic <- function(fun_name) {
  new_fun <- function(obj, ...) UseMethod(fun_name)
  assign(fun_name, new_fun, envir = .GlobalEnv)
}

cat("hi\n")
# hi

create_generic("cat")

cat("hi\n")
# Error in UseMethod(fun_name) : 
#   no applicable method for 'cat' applied to an object of class "character"

cat.character <- base::cat

cat("hi\n")
# hi
于 2017-12-27T22:03:40.717 回答
1

您还可以expr_interp()在函数中使用不带引号的运算符:

create_generic <- function(name, env = globalenv()) {
  gen <- expr_interp(function(obj, ...) {
    UseMethod(!!name)
  })

  environment(gen) <- env
  assign(name, gen, envir = env)
}

前缀是expr_因为它(内部)是通用的表达式包装器,例如公式和函数。

于 2017-12-28T13:36:14.910 回答