2016-03-22 12 views
6

Breve esempio. Sto esplorando il comportamento di una funzione provandola con diverse "specifiche", f(spec). Ho annotato una specifica a mano, spec1, e sto creando nuove specifiche come variazioni su di essa. Per fare questo, ho deciso di scrivere una funzione:Fabbriche di funzioni semplici e pulite in 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 

I miei obiettivi per make_spec sono ...

  1. all.equal(spec1, res1) e/o identical(spec1, res1)
  2. per str(res1) di essere leggibile (senza <environment: ptr> tag o)
  3. per evitare substitute e eval del tutto se possibile (non ad alta priorità)
  4. di evitare di scrivere il secondo arg di substitute (vedi esempio "pieno" di seguito)

C'è un modo idiomatico per raggiungere alcuni o tutti questi obiettivi?


Esempio completo. non sono sicuro se l'esempio precedente copre interamente il mio caso d'uso, quindi ecco il secondo:

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

Nota a margine. Non so se "fabbrica di funzioni" è il termine giusto qui, dal momento che io non sono uno scienziato informatico, ma sembra correlato. Ho trovato solo a paragraph on the concept related to R.

+0

In realtà sembra proprio non piace R e cercate un'altra lingua, Scala o Erlang forse .. La funzione 'str' è piuttosto complesso, ma si sono certamente benvenuto a riscriverlo. Quello sarebbe un grande progetto, ben al di sopra di quello che sarebbe una domanda ragionevole. L'idea di provare a programmare sulla lingua senza usare "eval" e "sostitutivi" sembra perversa e in nessun caso giustificata. –

+1

@ 42- Grazie per il feedback. Beh, evitando 'eval' e' sostitutivi' è in basso nella mia lista di priorità, ma se sto solo stupidamente trascurando un modo per evitarli, mi piacerebbe saperlo. – Frank

+1

è possibile ottenere la parte 1 (goffamente perché si restituisce più di una funzione) usando 'library (pryr)', 'res1 $ fx <- lapply (res1 $ fx, unenclose)', 'all.equal (res1, spec1) ' – Chris

risposta

3

Gli ambienti che racchiudono le funzioni sono diversi, il che porta alla differenza di output/differenza nel deparsing. Quindi, ci sono due cose da fare per ottenere il risultato desiderato:

  • fanno gli ambienti stesso
  • sostituto variabili dagli ambienti che racchiudono nei corpi di funzione.

Tuttavia, facendo in questo modo si ottiene una doppia dose di valutazione/sostituto che non si desidera, quindi forse ci sarebbe un'alternativa.

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

Grazie. Sto bene con la valutazione e il sostituto come usato qui e come questo approccio. Andando a lasciare la domanda aperta un po ', dato che non avrò la possibilità di applicare qualche soluzione al mio problema originale per un po'. – Frank