2016-03-22 7 views
6

短い例。私はさまざまな "specs"、f(spec)でテストすることによって、関数の動作を探求しています。私は手で1つのスペックを書きました、spec1、それにバリエーションとして新しいスペックを作成しています。これを行うために、私は関数を書くことにしました:str(res1)ためRのクリーンでシンプルなファクトリファクトリR

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. を人間が読めるように(無<environment: ptr>タグまたはsrcfilecopy
  3. 避けてくださいsubstituteeval可能であれば、それは重要です(優先度は高くない)
  4. の第二引数を書き出す避けるために、
  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

+0

は実際に、それはあなただけのRを好きではないし、本当に、おそらく別の言語、スカラ座やアーランを探しています表示されます。.. 'str'機能はかなり複雑ですが、あなたは確かにそれを書き換えるために歓迎されています。それは、合理的なSOの質問よりもはるかに大きなプロジェクトです。 'eval'と' substitute'を使わずに言語をプログラムしようとする考えは、逆転してどこでも正当化されているようです。 –

+1

@ 42-フィードバックありがとうございます。さて、私の優先順位のリストでは、「eval」と「substitute」を避けることはできませんが、私はそれらを避ける方法を愚かに見落としているだけです。 – Frank

+1

'ライブラリ(pryr)'、 'res1 $ fx -lapply(res1 $ fx、unenclose)'、 'all.equal(res1、spec1) 'を使ってパート1を得ることができます(単なる関数を返すので不器用です) ' – Chris

答えて

3

関数の囲む環境が異なり、出力の相違が解消されます。だから、所望の出力を得るために行う2つのものがあります。

  • は環境と同じ
  • 代替機能体へ囲む環境からの変数を作ります。

しかし、このようにすると、あなたが望んでいない評価版/代替品が2倍になるので、おそらく代替品があります。

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 
+0

ありがとうございます。私はここで使用されているように、このアプローチのように、評価と代替でうまくいっています。元の問題にしばらくの間解決してもらえないので、質問をしばらく開いておいてください。 – Frank