【问题标题】:how can I pass argument (names) to a function factory?如何将参数(名称)传递给函数工厂?
【发布时间】:2017-07-30 10:27:08
【问题描述】:

我需要用很多不同的参数构建很多函数,尽管它们共享很多代码和结构。 为了避免重复,我想我会很聪明,为自己构建一个函数工厂(又名闭包)。

我不知道如何在函数工厂中传递函数参数

我的用例是一堆 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)
}

显然,我希望避免在构造函数的 general 部分中出现重复,因此可以像这样使用的函数工厂会很好:

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

有没有办法做到这一点? 我做错了吗?

(对我来说,生成的类构造函数具有正确命名的参数对我来说非常重要,因此... 方法将不起作用)。

【问题讨论】:

标签: r functional-programming closures factory


【解决方案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()) 的想法取自 this question。请注意,沿着这条路你应该格外小心,正如?formals 所说,“这是 高级、危险的编码”。

【讨论】:

  • 啊太棒了,非常感谢,这完成了工作。您是否@tonytonov 或其他任何人知道为什么这被认为是“危险的”和/或有其他更安全的建议来完成这项工作?
  • @maxheld 不客气。好吧,到目前为止还不是超级危险,但关键是,搞乱bodyformals 并不是一件小事。这很模糊:下周给我看这个 sn-p,我可能不会理解它的目的。它很难维护,您可能会在以后的开发阶段期待一些怪癖(例如,如果您需要构造函数中的... 支持等,则需要额外的工作)。不代表这条路不好;对于像你这样的任务这很好,只是不要尝试实现你自己的完全面向对象的类系统:)
猜你喜欢
  • 2015-11-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多