R的清洁,简单的功能工厂

时间:2016-03-22 18:58:28

标签: r functional-programming

简短示例。我正在通过使用不同的“规范”f(spec)对其进行测试来探索函数的行为。我手写了一个规范,spec1,并创建了新的规格作为它的变体。为此,我决定编写一个函数:

spec1 = list(fy = list(a = 1), fx = list(f1 = function(x) 10-x, f2 = function(x) 2-x))

make_spec = function(f = function(x) 10-x, xtheta = 2)
    list(fy = list(a = 1), fx = list(f1 = f, f2 = function(x) xtheta-x))

res1 = make_spec()

# first problem: they don't match

    all.equal(res1,spec1)
    # [1] "Component “fx”: Component “f2”: target, current do not match when deparsed"
    # ^ this happens, even though...
    res1$fx$f2(4) == spec1$fx$f2(4)
    # TRUE

# second problem: res1 is fugly

    res1
    # $fy
    # $fy$a
    # [1] 1
    # 
    # 
    # $fx
    # $fx$f1
    # function (x) 
    # 10 - x
    # <environment: 0x000000000f8f2e20>
    # 
    # $fx$f2
    # function (x) 
    # xtheta - x
    # <environment: 0x000000000f8f2e20>

    str(res1)
    # even worse

make_spec的目标是......

  1. all.equal(spec1, res1)和/或identical(spec1, res1)
  2. str(res1)成为人类可读的(无<environment: ptr>标签或srcfilecopy
  3. 尽可能避免substituteeval(不是高优先级)
  4. 以避免写出substitute的第二个arg(请参阅下面的“完整”示例)
  5. 是否存在实现部分或全部目标的惯用方法?

    完整示例。我不确定上面的示例是否完全覆盖了我的用例,所以这是后者:

    spec0 = list(
        v_dist = list(
            pdf  = function(x) 1,
            cdf  = function(x) x,
            q    = function(x) x,
            supp = c(0,1)
        )
        ,
        ucondv_dist = {
            ucondv_dist = list()
            ucondv_dist$condmean    = function(v) 10-v
            ucondv_dist$pdf         = function(u,v) dnorm(u, ucondv_dist$condmean(v), 50)
            ucondv_dist$cdf         = function(u,v) pnorm(u, ucondv_dist$condmean(v), 50)
            ucondv_dist
        }
    )
    
    make_spec = function(ycondx_condmean = function(x) 10-x, ycondx_sd = 50){
    
      s = substitute(list(
        x_dist = list(
          pdf  = function(x) 1,
          cdf  = function(x) x,
          q  = function(x) x,
          supp = c(0,1)
        )
        ,
        ycondx_dist = {
          ycondx_dist = list()
          ycondx_dist$condmean  = ycondx_condmean
          ycondx_dist$pdf     = function(u,v) dnorm(u, ycondx_dist$condmean(v), ycondx_sd)
          ycondx_dist$cdf     = function(u,v) pnorm(u, ycondx_dist$condmean(v), ycondx_sd)
          ycondx_dist
        }
      )
      , list(ycondx_condmean=ycondx_condmean, ycondx_sd = ycondx_sd))
    
      eval(s, .GlobalEnv)
    }
    
    res0 = make_spec()
    

    旁注。我不知道“功能工厂”这里是否是正确的术语,因为我不是计算机科学家,但它似乎有关系。我发现只有a paragraph on the concept related to R

1 个答案:

答案 0 :(得分:3)

功能的封闭环境不同,导致脱落的输出/差异不同。因此,要获得所需的输出,有两件事要做:

  • 使环境相同
  • 将封闭环境中的变量替换为函数体。

然而,通过这种方式你可以得到你不想要的双倍剂量的eval /替代品,所以也许会有替代品。

make_spec <- function(f = function(x) 10-x, xtheta = 2) {
  e <- parent.frame()
  fixClosure <- function(func)
    eval(eval(substitute(substitute(func)), parent.frame()), e)

  list(fy = list(a = 1), fx = list(
    f1 = fixClosure(f), 
    f2 = fixClosure(function(x) xtheta-x)
  ))
}

spec1 <- list(fy = list(a = 1), fx = list(f1 = function(x) 10-x, f2 = function(x) 2-x))
res1 <- make_spec()

all.equal(res1, spec1)
[1] TRUE