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

Fra*_*ank 6 functional-programming r

简短的例子.我正在通过测试不同的"规格"来探索函数的行为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
Run Code Online (Sandbox Code Playgroud)

我的目标make_spec是......

  1. all.equal(spec1, res1) 和/或 identical(spec1, res1)
  2. 为了str(res1)人类可读(没有<environment: ptr>标签或srcfilecopy)
  3. 尽可能避免substituteeval完全(不是高优先级)
  4. 避免写出第二个arg substitute(参见下面的"完整"示例)

是否有一种惯用的方式来实现这些目标中的一些或全部?


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

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()
Run Code Online (Sandbox Code Playgroud)

旁注.我不知道"功能工厂"这里是否是正确的术语,因为我不是计算机科学家,但它似乎有关系.我在与R相关的概念中只找到了一个段落.

pic*_*ick 3

函数的封闭环境不同,导致输出的差异/解析的差异。因此,要获得所需的输出,需要做两件事:

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

然而,这样做你会得到双倍剂量的你不想要的评估/替代品,所以也许会有替代方案。

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
Run Code Online (Sandbox Code Playgroud)