2013-05-16 2 views
7

Zewnętrzny program potrzebuje pliku wejściowego z pewnymi parametrami kontrolnymi i chcę je wygenerować automatycznie za pomocą R. Zazwyczaj używam po prostu paste("parameter1: ", param1, ...), aby utworzyć długi ciąg tekstu, a wyjście do plik, ale skrypt szybko staje się nieczytelny. Problem ten jest prawdopodobnie dobrze nadaje się do Whisker,szablon bezpieczny przy winie z warzeniem/wiską

library(whisker) 

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

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

whisker.render(template, data) 

Mój problem tutaj jest to, że nie ma bezpiecznego sprawdzania, że ​​data zawiera wszystkie wymagane zmienne, np

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

po cichu zignoruje fakt, że zapomniałem podać nazwę. Jednak mój program końcowy ulegnie awarii, jeśli nie uda mi się utworzyć kompletnego pliku konfiguracyjnego.

Inny system szablonów jest dostarczany przez brew; ma tę zaletę, że faktycznie oceny rzeczy, a potencjalnie może to również pomóc wykryć zmienne brakuje,

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 

jednak siedzę z dwóch kwestii:

  • attach() ... detach() nie jest idealny, (daje ostrzeżenia co jakiś czas), a przynajmniej nie wiem, jak go właściwie używać. Próbowałem zdefiniować środowisko dla brew(), ale było zbyt restrykcyjne i nie znałem już funkcji base ...

  • mimo wystąpienia błędu, ciąg znaków jest nadal zwracany przez funkcję. Próbowałem opakować wywołanie w try(), ale nie mam doświadczenia w obsłudze błędów. Jak mogę powiedzieć, aby opuścił funkcję, która nie produkuje żadnych danych wyjściowych?

Edit: I zostały zaktualizowane rozwiązania brew do korzystania z nowego środowiska zamiast attach() i zatrzymać wykonywanie w przypadku wystąpienia błędu. (?capture.output sugeruje, że to nie była właściwa funkcja użyć tutaj, ponieważ „Podjęto próbę napisać wyjścia jak najdalej do pliku, jeśli jest błąd w ocenie wyrażenia” ...)

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

Musi być łatwiejszy sposób z tryCatch, ale nie mogę zrozumieć jednej rzeczy na jej stronie pomocy.

Z zadowoleniem przyjmuję inne sugestie dotyczące bardziej ogólnego problemu.

Odpowiedz

4

Korzystanie z wyrażeń regularnych do pobierania nazwy zmiennych z szablonu, można potwierdzić przed renderowaniem, np

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

Dzięki, że jedna opcja. Podoba mi się fakt, że brew ma zdolność oceny kodu. – baptiste

+0

Użyłem whisky w moim przykładzie, ale oczywiście można go łatwo zmienić do pracy z szablonami parzenia. –

+0

łatwo ... jeśli znasz wyrażenia regularne :) – baptiste

3

Nowy glue package zapewnia inną alternatywę,

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

Od wersji 1.1.0 (w CRAN 19 sierpnia 2016 r.), stringr package zawiera funkcję str_interp() (niestety nie wymienioną w pliku NEWS w wydaniu).

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

Przygotowując stringr answer zauważyłem, że pytania dotyczące OP za korzystanie z brew() nie została skierowana do tej pory. W szczególności OP był proszony o dostarczenie jego środowiska i sposobu zapobiegania zwracaniu łańcucha znaków w przypadku błędu.

OP stworzył funkcję own_brew(), która opakowuje połączenie pod numer brew(). Chociaż obecnie dostępny jest alternatywny pakiet, uważam, że oryginalne pytanie zasługuje na odpowiedź.

To moja próba poprawy 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) 
} 

głównych różnic jest to, że list2env() służy do przekazania wykazu values do brew() i że wezwanie do try() unika testując wartości zwracanej out dla błąd.

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]) :