how can I pass argument (names) to a function factory?

我的梦境 提交于 2019-12-22 10:59:55

问题


I need to build a lot of functions with lots of different arguments, though they otherwise share a lot of code and structure. To avoid duplication, I thought I'd be clever and build myself a function factory (aka closure).

I can't figure out how to pass the function arguments inside the function factory.

My use case is a bunch of S3 constructor functions, all of which share the same validation mechanism. So I'll use that as an example to explain my problem.

Say, I have a ClassA and ClassB, each of which require their own arguments in the respective constructor functions:

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)
}

Obviously, I'd love to avoid the duplication in the general part of the constructor functions, so a function factory that could be used like so would be nice:

ClassA <- produce_class_constructor(classname = "ClassA", fun = function(A_arg1, A_arg2) {return(list(A_arg1, A_arg2))})

This should, ideally, yield the exact same function as the above manually constructed ClassA function, with the general part factored out.

Here's my attempt at building that function factory:

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
  }
}

This however, does not work, because the resulting constructor function only has a class_specific_arguments-argument, not the, well, actual A_arg1, and A_arg2.

Is there way to do this? Am I doing this wrong?

(It's really important to me that the resulting class constructor functions have properly named arguments, so a ... approach won't work).


回答1:


Here's my attempt:

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"

Idea with as.list(environment()) taken from this question. Note that you should be extra careful along that path, as ?formals says, "this is advanced, dangerous coding".



来源:https://stackoverflow.com/questions/42695298/how-can-i-pass-argument-names-to-a-function-factory

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!