如何将参数(名称)传递给函数工厂?

3
我需要构建许多具有不同参数的函数,尽管它们共享大量代码和结构。 为了避免重复,我想聪明地构建一个函数工厂(也称为闭包)。 我无法弄清如何在函数工厂内传递函数参数
我的用例是一堆S3构造函数,它们都共享相同的验证机制。因此,我将使用它作为示例来说明我的问题。
例如,我有一个ClassAClassB,每个类在各自的构造函数中都需要自己的参数:
ClassA <- function(A_arg1, A_arg2) {
  # some class-SPECIFIC construction magic happens, say
  out <- list(A_arg1, A_arg2)

  # some GENERAL construction magic happens
  class(out) <- "ClassA"

  return(out)
}

ClassB <- function(B_arg1, B_arg2) {
  # some class-SPECIFIC construction magic happens, say
  out <- B_arg1 + B_arg2

  # some GENERAL construction magic happens
  class(out) <- "ClassB"

  return(out)
}

显然,我希望避免构造函数通用部分的重复,因此一个可以像这样使用的函数工厂将很不错:
ClassA <- produce_class_constructor(classname = "ClassA", fun = function(A_arg1, A_arg2) {return(list(A_arg1, A_arg2))})

理想情况下,这应该产生与手动构建的ClassA函数完全相同的函数,其中通用部分已被提取出来。

以下是我尝试构建的函数工厂:

produce_class_constructor <- function(classname, fun) {
  class_specific_arguments <- formals(fun = fun)  # this works just fine on the console
  construct_class <- function(class_specific_arguments) {
    # here runs the class-specific stuff
    out <- fun(class_specific_arguments)

    # here runs the general stuff
    class(out) <- classname
  }
}

然而,这样做是行不通的,因为生成的构造函数只有一个class_specific_arguments参数,而没有实际的A_arg1A_arg2参数。
有没有办法解决这个问题? 我做错了什么吗?
(对我来说,生成的类构造函数具有正确命名的参数非常重要,所以...方法行不通。)

我添加了一个类似的问题,我正在尝试弄清楚如何使用函数运算符(也称为point-free函数式编程)解决相同的问题,或许更加优雅。 - maxheld
1个回答

1

这是我的尝试:

produce_class_constructor <- function(classname, fun) {
  out_fun <- function() {
    out_obj <- do.call(fun, as.list(environment()))
    class(out_obj) <- classname
    out_obj
  }
  formals(out_fun) <- formals(fun)
  out_fun
}

ClassA <- produce_class_constructor(classname = "ClassA", 
  fun = function(A_arg1, A_arg2) {list(A_arg1, A_arg2)})
ClassA(1, 2)
#[[1]]
#[1] 1
#
#[[2]]
#[1] 2
#
#attr(,"class")
#[1] "ClassA"

ClassB <- produce_class_constructor(classname = "ClassB", 
  fun = function(B_arg1, B_arg2) {B_arg1 + B_arg2})
ClassB(B_arg2 = 2, 1)
#[1] 3
#attr(,"class")
#[1] "ClassB"

这个想法来自这个问题,使用as.list(environment())。请注意沿着这条路径要特别小心,因为?formals说,“这是高级、危险的编程”。


啊,太棒了,非常感谢,这可以完成工作。@tonytonov或其他人知道为什么这被认为是“危险”的吗?或者有另一个更安全的建议来完成这个任务吗? - maxheld
1
@maxheld 不用谢。目前来看,它并不是非常危险,但问题在于,操作 bodyformals 并不是一件微不足道的事情。这很难理解:下周再给我看这段代码,我可能就不明白它的目的了。它很难维护,而且在后期开发阶段可能会出现一些怪异的问题(比如,如果你需要在构造函数中支持 ...,那么可能需要进行额外的工作)。这并不意味着这条路不好;对于像你这样的任务来说,这是可以的,只是不要尝试实现自己完全面向对象的类系统 :) - tonytonov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接