2013-05-16 12 views
7

Un programma esterno necessita di un file di input con alcuni parametri di controllo, e desidero generare quelli automaticamente usando R. Di solito, uso semplicemente paste("parameter1: ", param1, ...) per creare la lunga stringa di testo e l'output per un file, ma lo script diventa rapidamente illeggibile. Questo problema è probabilmente adatto a Baffo,templating sicuro con brew/whisker

library(whisker) 

template= 'Hello {{name}} 
You have just won ${{value}}! 
' 

data <- list(name = "Chris", value= 124) 

whisker.render(template, data) 

mio problema qui, è che non c'è di sicurezza controllando che data contiene tutte le variabili richieste, ad esempio,

whisker.render(template, data[-1]) 

ignorerà silenziosamente il fatto che ho dimenticato di specificare un nome. Il mio programma finale si bloccherà, tuttavia, se non riesco a produrre un file di configurazione completo.

Un altro sistema di template è fornito da brew; ha il vantaggio di realmente valutare le cose, e potenzialmente questo può anche aiutare a rilevare le variabili mancanti,

library(brew) 

template2 = 'Hello <%= name %> 
You have just won $<%= value %>! 
' 

data <- list(name = "Chris", value= 124) 

own_brew <- function(template, values){ 
    attach(values, pos=2) 
    out = capture.output(brew(text = template)) 
    detach(values, pos=2) 
    cat(out, sep='\n') 
    invisible(out) 
} 

own_brew(template2, data) 
own_brew(template2, data[-1]) # error 

Tuttavia, sono bloccato con due problemi:

  • attach() ... detach() non è l'ideale, (dà avvertimenti ogni tanto), o almeno non so come usarlo correttamente. Ho provato a definire un ambiente per brew(), ma era troppo restrittivo e non conoscevo più le funzioni base ...

  • anche se si verifica un errore, una stringa viene comunque restituita dalla funzione. Ho provato a racchiudere la chiamata in try() ma non ho esperienza nella gestione degli errori. Come posso dire di uscire dalla funzione non producendo output?

Edit: Ho aggiornato la soluzione brew di utilizzare un nuovo ambiente, invece di attach(), e interrompere l'esecuzione in caso di errore. (?capture.output suggerisce che non era la funzione di diritto di utilizzare qui, dal momento che "Si è tentato di scrivere in uscita per quanto possibile, di presentare se c'è un errore nella valutazione delle espressioni" ...)

own_brew <- function(template, values, file=""){ 
    env <- as.environment(values) 
    parent.env(env) <- .GlobalEnv 
    a <- textConnection("cout", "w") 
    out <- try(brew(text = template, envir=env, output=a)) 

    if(inherits(out, "try-error")){ 
    close(a) 
    stop() 
    } 
    cat(cout, file=file, sep="\n") 
    close(a) 
    invisible(cout) 
} 

Ci deve essere un modo più semplice con tryCatch, ma non riesco a capire una cosa sola nella sua pagina di aiuto.

Accolgo con favore altri suggerimenti sul problema più generale.

risposta

4

Uso delle espressioni regolari per recuperare i nomi delle variabili dal modello, si potrebbe convalidare prima del rendering, ad esempio,

render <- function(template, data) { 
    vars <- unlist(regmatches(template, gregexpr('(?<=\\{\\{)[[:alnum:]_.]+(?=\\}\\})', template, perl=TRUE))) 
    stopifnot(all(vars %in% names(data))) 
    whisker.render(template, data) 
} 

render(template, data) 
+0

Grazie, questa è una possibilità. Mi piace il fatto che la birra abbia la capacità di valutare il codice. – baptiste

+0

Ho usato il baffo nel mio esempio, ma ovviamente questo potrebbe essere facilmente modificato per lavorare con i modelli di preparazione. –

+0

facilmente ... se si conoscono le espressioni regolari :) – baptiste

3

Il nuovo glue package fornisce un'altra alternativa,

library(glue) 
template <- 'Hello {name} You have just won ${value}!' 
data <- list(name = "Chris", value= 124) 

glue_data(template, .x=data) 
# Hello Chris You have just won $124! 

glue_data(template, .x=data[-1]) 
# Error in eval(expr, envir, enclos) : object 'name' not found 
1

A partire dalla versione 1.1.0 (il giorno 19 agosto 2016), lo stringr package include la funzione str_interp() (sfortunatamente non menzionata nel file NEWS della versione).

template <- "Hello ${name} You have just won $${value}!" 
data <- list(name = "Chris", value= 124) 

stringr::str_interp(template, data) 
[1] "Hello Chris You have just won $124!" 
stringr::str_interp(template, data[-1L]) 
Error in FUN(X[[i]], ...) : object 'name' not found 
1

Durante la preparazione del stringr answer ho notato che le domande di OP che regolano l'uso di brew() non è stata affrontata finora. In particolare, l'OP chiedeva come fornire il suo data all'ambiente e come impedire che una stringa di caratteri venisse restituita in caso di errore.

L'OP ha creato una funzione own_brew() che avvolge la chiamata a brew(). Anche se ora sono disponibili pacchetti alternativi, ritengo che la domanda originale meriti una risposta.

questo è il mio tentativo di migliorare baptiste's version:

own_brew <- function(template, values, file=""){ 
    a <- textConnection("cout", "w") 
    out <- brew::brew(text = template, envir=list2env(values), output=a) 
    close(a) 
    if (inherits(out, "try-error")) stop() 
    cat(cout, file=file, sep="\n") 
    invisible(cout) 
} 

Le differenze principali sono che list2env() viene utilizzato per passare l'elenco dei values a brew() e che la chiamata a try() è evitato da testare il valore di ritorno out per un errore.

template <- "Hello <%= name %> You have just won $<%= value %>!" 
data <- list(name = "Chris", value= 124) 

own_brew(template, data) 
Hello Chris You have just won $124! 
own_brew(template, data[-1L]) 
Error in cat(name) : object 'name' not found 
Error in own_brew(template, data[-1L]) :